@stack-spot/ai-chat-widget 1.28.0 → 1.28.1-beta.1

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.
Files changed (91) hide show
  1. package/dist/app-metadata.json +3 -3
  2. package/dist/components/AgentCard/dictionary.d.ts +4 -2
  3. package/dist/components/AgentCard/dictionary.d.ts.map +1 -1
  4. package/dist/components/AgentCard/dictionary.js +4 -2
  5. package/dist/components/AgentCard/dictionary.js.map +1 -1
  6. package/dist/components/Selector/index.d.ts +2 -2
  7. package/dist/components/Selector/index.d.ts.map +1 -1
  8. package/dist/components/Selector/index.js +2 -2
  9. package/dist/components/Selector/index.js.map +1 -1
  10. package/dist/layout.css +17 -0
  11. package/dist/state/types.d.ts +1 -1
  12. package/dist/state/types.d.ts.map +1 -1
  13. package/dist/utils/tools.d.ts +2 -2
  14. package/dist/utils/tools.d.ts.map +1 -1
  15. package/dist/utils/tools.js +3 -6
  16. package/dist/utils/tools.js.map +1 -1
  17. package/dist/utils/upload/UploadManager.d.ts +2 -0
  18. package/dist/utils/upload/UploadManager.d.ts.map +1 -1
  19. package/dist/utils/upload/UploadManager.js +32 -25
  20. package/dist/utils/upload/UploadManager.js.map +1 -1
  21. package/dist/utils/upload/use-paste-upload.d.ts +8 -0
  22. package/dist/utils/upload/use-paste-upload.d.ts.map +1 -0
  23. package/dist/utils/upload/use-paste-upload.js +19 -0
  24. package/dist/utils/upload/use-paste-upload.js.map +1 -0
  25. package/dist/views/Agents/AgentDescription.d.ts +2 -9
  26. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  27. package/dist/views/Agents/AgentDescription.js +12 -9
  28. package/dist/views/Agents/AgentDescription.js.map +1 -1
  29. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
  30. package/dist/views/Agents/AgentsPanel.js +11 -11
  31. package/dist/views/Agents/AgentsPanel.js.map +1 -1
  32. package/dist/views/Agents/AgentsTab.d.ts +2 -2
  33. package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
  34. package/dist/views/Agents/AgentsTab.js +6 -6
  35. package/dist/views/Agents/AgentsTab.js.map +1 -1
  36. package/dist/views/Agents/useAgentFavorites.d.ts +1 -1
  37. package/dist/views/Agents/useAgentFavorites.js +4 -4
  38. package/dist/views/Agents/useAgentFavorites.js.map +1 -1
  39. package/dist/views/Chat/ChatMessage.js +3 -3
  40. package/dist/views/Chat/ChatMessage.js.map +1 -1
  41. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
  42. package/dist/views/ChatHistory/HistoryItem.js +8 -5
  43. package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
  44. package/dist/views/ChatHistory/utils.d.ts +0 -6
  45. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  46. package/dist/views/ChatHistory/utils.js +1 -16
  47. package/dist/views/ChatHistory/utils.js.map +1 -1
  48. package/dist/views/Home/CustomAgent.js +3 -3
  49. package/dist/views/Home/CustomAgent.js.map +1 -1
  50. package/dist/views/MessageInput/AgentSelector.js +4 -4
  51. package/dist/views/MessageInput/AgentSelector.js.map +1 -1
  52. package/dist/views/MessageInput/ButtonAgent.js +2 -2
  53. package/dist/views/MessageInput/ButtonAgent.js.map +1 -1
  54. package/dist/views/MessageInput/UploadDragNDrop.d.ts +14 -0
  55. package/dist/views/MessageInput/UploadDragNDrop.d.ts.map +1 -0
  56. package/dist/views/MessageInput/UploadDragNDrop.js +52 -0
  57. package/dist/views/MessageInput/UploadDragNDrop.js.map +1 -0
  58. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  59. package/dist/views/MessageInput/dictionary.d.ts.map +1 -1
  60. package/dist/views/MessageInput/dictionary.js +9 -0
  61. package/dist/views/MessageInput/dictionary.js.map +1 -1
  62. package/dist/views/MessageInput/index.d.ts.map +1 -1
  63. package/dist/views/MessageInput/index.js +13 -1
  64. package/dist/views/MessageInput/index.js.map +1 -1
  65. package/dist/views/Tools.js +3 -3
  66. package/dist/views/Tools.js.map +1 -1
  67. package/dist/views/Workspaces/WorkspacesTab.js +1 -1
  68. package/package.json +2 -2
  69. package/src/app-metadata.json +3 -3
  70. package/src/components/AgentCard/dictionary.ts +4 -2
  71. package/src/components/Selector/index.tsx +4 -5
  72. package/src/layout.css +17 -0
  73. package/src/state/types.ts +1 -1
  74. package/src/utils/tools.ts +5 -7
  75. package/src/utils/upload/UploadManager.ts +32 -23
  76. package/src/utils/upload/use-paste-upload.tsx +30 -0
  77. package/src/views/Agents/AgentDescription.tsx +19 -25
  78. package/src/views/Agents/AgentsPanel.tsx +11 -12
  79. package/src/views/Agents/AgentsTab.tsx +12 -17
  80. package/src/views/Agents/useAgentFavorites.ts +4 -4
  81. package/src/views/Chat/ChatMessage.tsx +3 -3
  82. package/src/views/ChatHistory/HistoryItem.tsx +10 -5
  83. package/src/views/ChatHistory/utils.ts +1 -18
  84. package/src/views/Home/CustomAgent.tsx +4 -4
  85. package/src/views/MessageInput/AgentSelector.tsx +4 -4
  86. package/src/views/MessageInput/ButtonAgent.tsx +2 -2
  87. package/src/views/MessageInput/UploadDragNDrop.tsx +83 -0
  88. package/src/views/MessageInput/dictionary.ts +9 -0
  89. package/src/views/MessageInput/index.tsx +17 -1
  90. package/src/views/Tools.tsx +4 -3
  91. package/src/views/Workspaces/WorkspacesTab.tsx +1 -1
@@ -1,15 +1,15 @@
1
1
  /* eslint-disable filenames/match-regex */
2
- import { agentClient } from '@stack-spot/portal-network'
2
+ import { agentClient, agentToolsClient } from '@stack-spot/portal-network'
3
3
 
4
4
  export function useAgentFavorites() {
5
- const useFavorites = () => agentClient.allAgents.useQuery({ visibilities: ['FAVORITE'] })
5
+ const useFavorites = () => agentToolsClient.agents.useQuery({ visibility: 'favorite' })
6
6
  const [addFavorite, pendingAddFav] = agentClient.addFavoriteAgent.useMutation()
7
7
  const [removeFavorite, pendingRemoveFav] = agentClient.removeFavoriteAgent.useMutation()
8
8
 
9
9
  const removeFavoriteAgent = async (idOrSlug?: string) => {
10
10
  try {
11
11
  await removeFavorite({ agentId: idOrSlug || '' })
12
- await agentClient.allAgents.invalidate({ visibilities: ['FAVORITE'] })
12
+ await agentToolsClient.agents.invalidate({ visibility: 'favorite' })
13
13
  } catch (error) {
14
14
  // eslint-disable-next-line no-console
15
15
  console.error(error)
@@ -19,7 +19,7 @@ export function useAgentFavorites() {
19
19
  const addFavoriteAgent = async (idOrSlug?: string) => {
20
20
  try {
21
21
  await addFavorite({ agentId: idOrSlug || '' })
22
- await agentClient.allAgents.invalidate({ visibilities: ['FAVORITE'] })
22
+ await agentToolsClient.agents.invalidate({ visibility: 'favorite' })
23
23
  } catch (error) {
24
24
  // eslint-disable-next-line no-console
25
25
  console.error(error)
@@ -1,7 +1,7 @@
1
1
  import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
2
2
  import { Check, Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
3
3
  import { Badge, IconButton, Tooltip } from '@citric/ui'
4
- import { agentClient } from '@stack-spot/portal-network'
4
+ import { agentToolsClient } from '@stack-spot/portal-network'
5
5
  import { listToClass } from '@stack-spot/portal-theme'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
7
  import { groupBy } from 'lodash'
@@ -193,7 +193,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
193
193
  const widget = useWidget()
194
194
  const chat = useCurrentChat()
195
195
  const agentId = entry.agent?.id ?? ''
196
- const [agent] = agentClient.agentById.useStatefulQuery({ agentId, builtIn: !!entry?.agent?.builtIn }, { enabled: !!agentId })
196
+ const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!agentId })
197
197
  const [copied, setCopied] = useState(false)
198
198
  const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
199
199
 
@@ -348,7 +348,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
348
348
  className="tools-badge"
349
349
  label={t.tools}
350
350
  images={entry.tools.slice(0, 3).map((id) => {
351
- const tool = toolById(id, agent?.toolkits)
351
+ const tool = toolById(id, toolKits)
352
352
  return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
353
353
  })}
354
354
  onClick={openToolsPanel}
@@ -2,7 +2,7 @@ import { IconBox, Input } from '@citric/core'
2
2
  import { Check, Download, EllipsisHorizontal, Pencil, Trash } from '@citric/icons'
3
3
  import { IconButton, LoadingCircular } from '@citric/ui'
4
4
  import { focusNextIgnoringChildren } from '@stack-spot/portal-components'
5
- import { aiClient } from '@stack-spot/portal-network'
5
+ import { agentToolsClient, aiClient } from '@stack-spot/portal-network'
6
6
  import { ConversationResponse } from '@stack-spot/portal-network/api/ai'
7
7
  import { theme } from '@stack-spot/portal-theme'
8
8
  import { last } from 'lodash'
@@ -11,12 +11,13 @@ import { OverlayMenu } from '../../components/OverlayMenu'
11
11
  import { useWidget } from '../../context/hooks'
12
12
  import { ChatEntry } from '../../state/ChatEntry'
13
13
  import { ChatState } from '../../state/ChatState'
14
+ import { LabeledAgent } from '../../state/types'
14
15
  import { ButtonAction } from '../../types'
15
16
  import { download } from '../../utils/download'
16
17
  import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
17
18
  import { useHistoryDictionary } from './dictionary'
18
19
  import { HistoryItemBox } from './styled'
19
- import { findStack, findWorkspace, getAllAgents } from './utils'
20
+ import { findStack, findWorkspace } from './utils'
20
21
 
21
22
  /**
22
23
  * Renders an item of the list of conversations (history).
@@ -88,12 +89,16 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
88
89
  setLoading(true)
89
90
  try {
90
91
  const chat = await aiClient.chat.query({ conversationId: item.id })
92
+ const historyAgentIds = chat.history?.map(item => item.custom_agent?.id).filter(Boolean) as string[] ?? []
91
93
  const [stack, workspace, agents] = await Promise.all([
92
94
  findStack(chat.ai_stack_id),
93
95
  findWorkspace(chat.workspace_id),
94
- getAllAgents(),
96
+ agentToolsClient.agentsByIds.query({ searchAgentsRequest: { ids: historyAgentIds } }),
95
97
  ])
96
- const agent = agents.find(a => a.id === last(chat.history)?.custom_agent?.id)
98
+ const agentsAsLabeledAgents: LabeledAgent[] = agents
99
+ .map((a) => ({ ...a, label: a.name, image: a.avatar ?? '', builtIn: a.visibility_level === 'built_in' }))
100
+
101
+ const agent = agentsAsLabeledAgents.find(a => a.id === last(chat.history)?.custom_agent?.id)
97
102
  const builtIn = !!last(chat.history ?? [])?.custom_agent?.built_in
98
103
  widget.chatTabs.add(new ChatState({
99
104
  id: chat.id,
@@ -103,7 +108,7 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
103
108
  agentType: item.agent === 'USER' ? 'user' : 'bot',
104
109
  content: item.content,
105
110
  type: 'md',
106
- agent: agents.find(a => a.id === item.custom_agent?.id),
111
+ agent: agentsAsLabeledAgents.find(a => a.id === item.custom_agent?.id),
107
112
  messageId: item.message_id,
108
113
  knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
109
114
  updated: item.updated,
@@ -1,6 +1,5 @@
1
- import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
1
+ import { aiClient, workspaceClient } from '@stack-spot/portal-network'
2
2
  import { ChatProperties } from '../../state/ChatState'
3
- import { LabeledWithImage } from '../../state/types'
4
3
 
5
4
  /**
6
5
  * Finds a stack by its id.
@@ -36,19 +35,3 @@ export async function findWorkspace(id: string | null): Promise<ChatProperties['
36
35
  return { id, label: id }
37
36
  }
38
37
  }
39
-
40
-
41
- /**
42
- * Finds all the agents, including common agents and public agents.
43
- * @returns an array with every agent.
44
- */
45
- export async function getAllAgents(): Promise<LabeledWithImage[]> {
46
- try {
47
- const agents = await agentClient.allAgents.query({ visibilities: ['ALL'] })
48
- return agents.map(a => ({ id: a.id, label: a.name, image: a.avatar, slug: a.slug, builtIn: a.builtIn }))
49
- } catch (error) {
50
- // eslint-disable-next-line no-console
51
- console.error(error)
52
- return []
53
- }
54
- }
@@ -1,6 +1,6 @@
1
1
  import { IconBox, Text } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
- import { agentClient } from '@stack-spot/portal-network'
3
+ import { agentToolsClient } from '@stack-spot/portal-network'
4
4
  import { useMemo } from 'react'
5
5
  import { QuickStartButton } from '../../components/QuickStartButton'
6
6
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
@@ -12,15 +12,15 @@ import { HomeBox } from './styled'
12
12
  */
13
13
  export const CustomAgent = () => {
14
14
  const { id, label, image } = useCurrentChatState('agent') ?? {}
15
- const [agent] = agentClient.agent.useStatefulQuery({ agentId: id! })
15
+ const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: id! })
16
16
  const chat = useCurrentChat()
17
- const suggestions = useMemo(() => agent?.suggested_prompts?.map((prompt, index) => (
17
+ const suggestions = useMemo(() => agent?.conversation_starter?.map((prompt, index) => (
18
18
  <QuickStartButton
19
19
  key={index}
20
20
  label={prompt}
21
21
  onClick={() => send(prompt)}
22
22
  />
23
- )), [agent?.suggested_prompts])
23
+ )), [agent?.conversation_starter])
24
24
 
25
25
  function send(message: string) {
26
26
  chat.pushMessage(ChatEntry.createUserEntry(message))
@@ -1,6 +1,6 @@
1
1
  import { Flex, IconBox, Image } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
- import { agentClient, AgentResponseWithBuiltIn } from '@stack-spot/portal-network'
3
+ import { AgentResponseWithBuiltIn, agentToolsClient } from '@stack-spot/portal-network'
4
4
  import { useCallback } from 'react'
5
5
  import { Selector } from '../../components/Selector'
6
6
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
@@ -53,9 +53,9 @@ export const AgentSelector = ({ inputRef, isTrial }: {
53
53
 
54
54
  const getAgents = () => {
55
55
  if (isTrial) {
56
- return agentClient.allAgents.useQuery({ visibilities: ['PERSONAL', 'BUILT-IN'] })
56
+ return agentToolsClient.allAgents.useQuery({ visibilities: ['personal', 'built_in'] })
57
57
  }
58
- return agentClient.allAgents.useQuery({ visibilities: ['ACCOUNT', 'SHARED', 'PERSONAL', 'BUILT-IN', 'WORKSPACE'] })
58
+ return agentToolsClient.allAgents.useQuery({ visibilities: ['account', 'shared', 'personal', 'built_in', 'workspace'] })
59
59
  }
60
60
 
61
61
  return <Selector
@@ -68,7 +68,7 @@ export const AgentSelector = ({ inputRef, isTrial }: {
68
68
  regex: agentRegex,
69
69
  urlBuilder: (agent) => `/agents/${agent?.id}`,
70
70
  searchProp: 'name',
71
- sections: isTrial ? ['favorite', 'personal', 'built-in'] : ['favorite', 'personal', 'workspace', 'account', 'shared', 'built-in'],
71
+ sections: isTrial ? ['favorite', 'personal', 'built_in'] : ['favorite', 'personal', 'workspace', 'account', 'shared', 'built_in'],
72
72
  renderComponentItem: AgentItem,
73
73
  isEnabled: isAgentEnabled,
74
74
  onSelect: onSelectItem,
@@ -2,14 +2,14 @@ import { Flex, IconBox } from '@citric/core'
2
2
  import { Agent, TimesMini } from '@citric/icons'
3
3
  import { IconButton, Tooltip } from '@citric/ui'
4
4
  import { MiniLogo } from '@stack-spot/portal-components/svg'
5
- import { agentClient } from '@stack-spot/portal-network'
5
+ import { agentToolsClient } from '@stack-spot/portal-network'
6
6
  import { useEffect } from 'react'
7
7
  import { useCurrentChat, useCurrentChatState, useWidget } from '../../context/hooks'
8
8
  import { useMessageInputDictionary } from './dictionary'
9
9
 
10
10
  export const ButtonAgent = () => {
11
11
  const t = useMessageInputDictionary()
12
- const agentDefault = agentClient.agentDefault.useQuery({})
12
+ const agentDefault = agentToolsClient.agentDefault.useQuery()
13
13
  const widget = useWidget()
14
14
  const chat = useCurrentChat()
15
15
  const agent = useCurrentChatState('agent')
@@ -0,0 +1,83 @@
1
+ import { IconBox } from '@citric/core'
2
+ import { Document } from '@citric/icons'
3
+ import { DragEvent, useCallback, useEffect, useState } from 'react'
4
+ import { useCurrentChat } from '../../context/hooks'
5
+ import { useMessageInputDictionary } from './dictionary'
6
+
7
+ export function useUploadDragDrop(containerSelector: string = '.chat-container') {
8
+ const [isDragging, setIsDragging] = useState(false)
9
+ const chat = useCurrentChat()
10
+
11
+ const handleDrop = useCallback((e: DragEvent) => {
12
+ e.preventDefault()
13
+ e.stopPropagation()
14
+ setIsDragging(false)
15
+ if (e.dataTransfer?.files?.length) {
16
+ chat.uploadManager.add(Array.from(e.dataTransfer.files))
17
+ }
18
+ }, [chat])
19
+
20
+ const handleDragLeave = useCallback((e: DragEvent) => {
21
+ e.preventDefault()
22
+ if (e.currentTarget && !e.currentTarget.contains(e.relatedTarget as Node)) {
23
+ setIsDragging(false)
24
+ }
25
+ }, [])
26
+
27
+ useEffect(() => {
28
+ const container = document.querySelector(containerSelector) as HTMLDivElement | null
29
+ if (!container) return
30
+
31
+ const nativeHandleDragEnter = (e: Event) => {
32
+ e.preventDefault()
33
+ setIsDragging(true)
34
+ }
35
+
36
+ container.addEventListener('dragenter', nativeHandleDragEnter)
37
+ return () => {
38
+ container.removeEventListener('dragenter', nativeHandleDragEnter)
39
+ }
40
+ }, [containerSelector])
41
+
42
+ return {
43
+ isDragging,
44
+ handleDrop,
45
+ handleDragLeave,
46
+ }
47
+ }
48
+
49
+ interface UploadDragNDropProps {
50
+ isDragging?: boolean,
51
+ onDrop?: (e: DragEvent) => void,
52
+ onDragLeave?: (e: DragEvent) => void,
53
+ }
54
+
55
+ export const UploadDragNDrop = ({ isDragging, onDrop, onDragLeave }: UploadDragNDropProps) => {
56
+ const t = useMessageInputDictionary()
57
+
58
+ if (!isDragging) return null
59
+
60
+ return (
61
+ <div
62
+ className="upload-drag-drop"
63
+ data-active={isDragging}
64
+ role="dialog"
65
+ aria-modal="true"
66
+ aria-labelledby="upload-drag-drop-title"
67
+ aria-describedby="upload-drag-drop-description"
68
+ onDrop={onDrop}
69
+ onDragLeave={onDragLeave}
70
+ onDragOver={e => {
71
+ e.preventDefault()
72
+ e.stopPropagation()
73
+ }}
74
+ tabIndex={-1}
75
+ >
76
+ <IconBox size="lg" aria-hidden={true}>
77
+ <Document />
78
+ </IconBox>
79
+ <h2 id="upload-drag-drop-title">{t.uploadDragDropTitle}</h2>
80
+ <p id="upload-drag-drop-description">{t.uploadDragDropDescription}</p>
81
+ </div>
82
+ )
83
+ }
@@ -9,6 +9,7 @@ const dictionary = {
9
9
  agent: 'Select agent',
10
10
  send: 'Send message',
11
11
  placeholder: 'Send a message to $0. For commands, use @ or /',
12
+ typing: 'Type your message...',
12
13
  cancel: 'Cancel',
13
14
  removeConfig: 'Remove all the configuration',
14
15
  removeStack: 'Stop using the current stack',
@@ -22,6 +23,10 @@ const dictionary = {
22
23
  collapse: 'Collapse options',
23
24
  uploadSizeError: 'The following files were not added to the upload list because they\'re larger than $1:',
24
25
  uploadItemLimitError: 'The following files were not added because no more than $1 items may be uploaded at a time:',
26
+ uploadError: 'An error occurred while uploading the file "$0".',
27
+ uploadDragging: 'Drop the file here to upload',
28
+ uploadDragDropTitle: 'Arraste e solte arquivos',
29
+ uploadDragDropDescription: 'Solte arquivos aqui para enviá-los na conversa',
25
30
  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
31
  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
32
  cantSendBecauseOfEmptyContent: 'You can\'t send empty messages. Please write some text or upload a file.',
@@ -35,6 +40,7 @@ const dictionary = {
35
40
  agent: 'Selecionar agente',
36
41
  send: 'Enviar mensagem',
37
42
  placeholder: 'Envie uma mensagem para $0. Para comandos, use @ ou /',
43
+ typing: 'Digite sua mensagem...',
38
44
  cancel: 'Cancelar',
39
45
  removeConfig: 'Remover todas as configurações',
40
46
  removeStack: 'Parar de usar a stack atual',
@@ -49,6 +55,9 @@ const dictionary = {
49
55
  uploadSizeError: 'Os seguintes arquivos não foram adicionados à lista de upload porque eles são maiores que $0:',
50
56
  uploadItemLimitError: 'Os seguintes arquivos não foram adicionados à lista de upload porque é permitido enviar no máximo $0 arquivos por vez:',
51
57
  uploadError: 'Ocorreu um erro ao enviar o arquivo "$0".',
58
+ uploadDragging: 'Solte o arquivo aqui para fazer upload',
59
+ uploadDragDropTitle: 'Solte seus arquivos aqui',
60
+ uploadDragDropDescription: 'Arraste ou solte arquivos para compartilhar na conversa',
52
61
  unknownUploadError: 'Ocorreu um erro ao enviar os arquivos.',
53
62
  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.',
54
63
  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.',
@@ -8,6 +8,7 @@ import { quickCommandRegex } from '../../regex'
8
8
  import { ChatEntry } from '../../state/ChatEntry'
9
9
  import { checkIsTrial } from '../../utils/check-is-trial'
10
10
  import { UploadProvider } from '../../utils/upload/context'
11
+ import { usePasteUpload } from '../../utils/upload/use-paste-upload'
11
12
  import { AgentSelector } from './AgentSelector'
12
13
  import { ButtonAgent } from './ButtonAgent'
13
14
  import { ButtonBar } from './ButtonBar'
@@ -17,6 +18,7 @@ import { useMessageInputDictionary } from './dictionary'
17
18
  import { QuickCommandSelector } from './QuickCommandSelector'
18
19
  import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled'
19
20
  import { UploadBar } from './UploadBar'
21
+ import { UploadDragNDrop, useUploadDragDrop } from './UploadDragNDrop'
20
22
 
21
23
  /**
22
24
  * This renders the MessageInput part of the layout which includes the progress bar, the actual textarea, the badges telling what is
@@ -36,6 +38,13 @@ export const MessageInput = () => {
36
38
  const agentLabel = useCurrentChatState('agent')?.label
37
39
  const { handleKeyDown, handleKeyUp } = useUserEntryHistoryShortcut()
38
40
  const isTrial = checkIsTrial()
41
+ const { isDragging, handleDrop, handleDragLeave } = useUploadDragDrop()
42
+
43
+ usePasteUpload({
44
+ textAreaRef,
45
+ onUploadFiles: files => chat.uploadManager.add(files),
46
+ enabled: !isLoading,
47
+ })
39
48
 
40
49
  const checkSendRequirements = useCallback(() => {
41
50
  if (chat.uploadManager.status === 'error') {
@@ -112,7 +121,13 @@ export const MessageInput = () => {
112
121
  <ButtonAgent />
113
122
  <AdaptiveTextArea
114
123
  ref={textAreaRef}
115
- placeholder={agentLabel && interpolate(t.placeholder, agentLabel)}
124
+ placeholder={
125
+ isDragging
126
+ ? t.uploadDragging
127
+ : agentLabel
128
+ ? interpolate(t.placeholder, agentLabel)
129
+ : t.typing
130
+ }
116
131
  onChange={e => chat.set('nextMessage', e.target.value)}
117
132
  value={value}
118
133
  onFocus={() => setFocused(true)}
@@ -130,6 +145,7 @@ export const MessageInput = () => {
130
145
  <ContextBar />
131
146
  {chat.get('features').upload && <UploadBar />}
132
147
  <ButtonBar onSend={onSend} isLoading={isLoading} />
148
+ <UploadDragNDrop isDragging={isDragging} onDrop={handleDrop} onDragLeave={handleDragLeave} />
133
149
  </MessageInputBox>
134
150
  </UploadProvider>
135
151
  )
@@ -1,4 +1,4 @@
1
- import { agentClient } from '@stack-spot/portal-network'
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
- const [agent] = agentClient.agent.useStatefulQuery({ agentId: message?.agent?.id ?? '' }, { enabled: !!message?.agent?.id })
44
- const tools = useMemo(() => message?.tools?.map(id => toolById(id, agent?.toolkits) ?? { id }), [messageId, agent])
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: 'WORKSPACE', agent, workspaceId, showSubmitButton }, fullScreen: true })
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 })