@stack-spot/ai-chat-widget 0.2.0 → 0.4.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.
Files changed (148) hide show
  1. package/dist/StackspotAIWidget.d.ts.map +1 -1
  2. package/dist/StackspotAIWidget.js +2 -1
  3. package/dist/StackspotAIWidget.js.map +1 -1
  4. package/dist/chat-interceptors/quick-commands.js +2 -2
  5. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  6. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  7. package/dist/chat-interceptors/send-message.js +6 -9
  8. package/dist/chat-interceptors/send-message.js.map +1 -1
  9. package/dist/components/HistoryList.d.ts +2 -5
  10. package/dist/components/HistoryList.d.ts.map +1 -1
  11. package/dist/components/HistoryList.js +70 -2
  12. package/dist/components/HistoryList.js.map +1 -1
  13. package/dist/components/OverlayMenu.d.ts +3 -2
  14. package/dist/components/OverlayMenu.d.ts.map +1 -1
  15. package/dist/components/OverlayMenu.js +57 -1
  16. package/dist/components/OverlayMenu.js.map +1 -1
  17. package/dist/components/Tooltip/Tooltip.d.ts +2 -1
  18. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  19. package/dist/components/Tooltip/Tooltip.js +10 -2
  20. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  21. package/dist/components/Tooltip/TooltipAPI.d.ts +5 -2
  22. package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -1
  23. package/dist/components/Tooltip/TooltipAPI.js +50 -8
  24. package/dist/components/Tooltip/TooltipAPI.js.map +1 -1
  25. package/dist/components/Tooltip/context.js +1 -1
  26. package/dist/components/Tooltip/context.js.map +1 -1
  27. package/dist/components/Tooltip/style.d.ts.map +1 -1
  28. package/dist/components/Tooltip/style.js +0 -1
  29. package/dist/components/Tooltip/style.js.map +1 -1
  30. package/dist/components/Tooltip/types.d.ts +6 -0
  31. package/dist/components/Tooltip/types.d.ts.map +1 -1
  32. package/dist/features.d.ts.map +1 -1
  33. package/dist/features.js +1 -0
  34. package/dist/features.js.map +1 -1
  35. package/dist/layout.css +6 -0
  36. package/dist/state/ChatEntry.d.ts +3 -2
  37. package/dist/state/ChatEntry.d.ts.map +1 -1
  38. package/dist/state/ChatEntry.js +2 -2
  39. package/dist/state/ChatEntry.js.map +1 -1
  40. package/dist/state/ChatState.d.ts +1 -7
  41. package/dist/state/ChatState.d.ts.map +1 -1
  42. package/dist/state/ChatState.js.map +1 -1
  43. package/dist/state/types.d.ts +8 -0
  44. package/dist/state/types.d.ts.map +1 -0
  45. package/dist/state/types.js +2 -0
  46. package/dist/state/types.js.map +1 -0
  47. package/dist/types.d.ts +1 -1
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/utils/date.d.ts +1 -0
  50. package/dist/utils/date.d.ts.map +1 -1
  51. package/dist/utils/date.js +3 -0
  52. package/dist/utils/date.js.map +1 -1
  53. package/dist/utils/download.d.ts +2 -0
  54. package/dist/utils/download.d.ts.map +1 -0
  55. package/dist/utils/download.js +10 -0
  56. package/dist/utils/download.js.map +1 -0
  57. package/dist/utils/knowledge-source.d.ts +3 -1
  58. package/dist/utils/knowledge-source.d.ts.map +1 -1
  59. package/dist/utils/knowledge-source.js +8 -0
  60. package/dist/utils/knowledge-source.js.map +1 -1
  61. package/dist/views/Agents.js +2 -1
  62. package/dist/views/Agents.js.map +1 -1
  63. package/dist/views/Chat/AgentInfo.d.ts +3 -2
  64. package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
  65. package/dist/views/Chat/AgentInfo.js +3 -3
  66. package/dist/views/Chat/AgentInfo.js.map +1 -1
  67. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  68. package/dist/views/Chat/ChatMessage.js +3 -3
  69. package/dist/views/Chat/ChatMessage.js.map +1 -1
  70. package/dist/views/Chat/chat-scroll.d.ts.map +1 -0
  71. package/dist/{hooks → views/Chat}/chat-scroll.js +5 -3
  72. package/dist/views/Chat/chat-scroll.js.map +1 -0
  73. package/dist/views/Chat/styled.d.ts.map +1 -1
  74. package/dist/views/Chat/styled.js +8 -1
  75. package/dist/views/Chat/styled.js.map +1 -1
  76. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts +5 -0
  77. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts.map +1 -0
  78. package/dist/views/ChatHistory/ChatHistoryPanel.js +10 -0
  79. package/dist/views/ChatHistory/ChatHistoryPanel.js.map +1 -0
  80. package/dist/views/ChatHistory/HistoryItem.d.ts +7 -0
  81. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -0
  82. package/dist/views/ChatHistory/HistoryItem.js +116 -0
  83. package/dist/views/ChatHistory/HistoryItem.js.map +1 -0
  84. package/dist/views/ChatHistory/dictionary.d.ts +2 -0
  85. package/dist/views/ChatHistory/dictionary.d.ts.map +1 -0
  86. package/dist/views/ChatHistory/dictionary.js +19 -0
  87. package/dist/views/ChatHistory/dictionary.js.map +1 -0
  88. package/dist/views/ChatHistory/index.d.ts +5 -0
  89. package/dist/views/ChatHistory/index.d.ts.map +1 -0
  90. package/dist/views/ChatHistory/index.js +23 -0
  91. package/dist/views/ChatHistory/index.js.map +1 -0
  92. package/dist/views/ChatHistory/styled.d.ts +2 -0
  93. package/dist/views/ChatHistory/styled.d.ts.map +1 -0
  94. package/dist/views/ChatHistory/styled.js +60 -0
  95. package/dist/views/ChatHistory/styled.js.map +1 -0
  96. package/dist/views/ChatHistory/utils.d.ts +5 -0
  97. package/dist/views/ChatHistory/utils.d.ts.map +1 -0
  98. package/dist/views/ChatHistory/utils.js +39 -0
  99. package/dist/views/ChatHistory/utils.js.map +1 -0
  100. package/dist/views/ChatTabSelection.js +1 -1
  101. package/dist/views/ChatTabSelection.js.map +1 -1
  102. package/dist/views/KnowledgeSources.d.ts.map +1 -1
  103. package/dist/views/KnowledgeSources.js +30 -21
  104. package/dist/views/KnowledgeSources.js.map +1 -1
  105. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  106. package/dist/views/Stacks.js +2 -1
  107. package/dist/views/Stacks.js.map +1 -1
  108. package/dist/views/Workspaces.d.ts.map +1 -1
  109. package/dist/views/Workspaces.js +3 -2
  110. package/dist/views/Workspaces.js.map +1 -1
  111. package/package.json +3 -2
  112. package/src/StackspotAIWidget.tsx +3 -1
  113. package/src/chat-interceptors/quick-commands.ts +2 -2
  114. package/src/chat-interceptors/send-message.ts +6 -9
  115. package/src/components/HistoryList.tsx +80 -7
  116. package/src/components/OverlayMenu.tsx +70 -3
  117. package/src/components/Tooltip/Tooltip.tsx +13 -7
  118. package/src/components/Tooltip/TooltipAPI.ts +47 -9
  119. package/src/components/Tooltip/context.tsx +1 -1
  120. package/src/components/Tooltip/style.tsx +0 -1
  121. package/src/components/Tooltip/types.ts +7 -0
  122. package/src/features.ts +1 -0
  123. package/src/layout.css +6 -0
  124. package/src/state/ChatEntry.ts +5 -4
  125. package/src/state/ChatState.ts +1 -9
  126. package/src/state/types.ts +8 -0
  127. package/src/types.ts +1 -1
  128. package/src/utils/date.ts +4 -0
  129. package/src/utils/download.ts +12 -0
  130. package/src/utils/knowledge-source.ts +13 -1
  131. package/src/views/Agents.tsx +2 -1
  132. package/src/views/Chat/AgentInfo.tsx +8 -8
  133. package/src/views/Chat/ChatMessage.tsx +4 -4
  134. package/src/{hooks → views/Chat}/chat-scroll.ts +6 -3
  135. package/src/views/Chat/styled.ts +8 -1
  136. package/src/views/ChatHistory/ChatHistoryPanel.tsx +28 -0
  137. package/src/views/ChatHistory/HistoryItem.tsx +139 -0
  138. package/src/views/ChatHistory/dictionary.ts +20 -0
  139. package/src/views/ChatHistory/index.tsx +31 -0
  140. package/src/views/ChatHistory/styled.ts +60 -0
  141. package/src/views/ChatHistory/utils.ts +37 -0
  142. package/src/views/ChatTabSelection.tsx +1 -1
  143. package/src/views/KnowledgeSources.tsx +39 -20
  144. package/src/views/Stacks.tsx +2 -1
  145. package/src/views/Workspaces.tsx +3 -2
  146. package/dist/hooks/chat-scroll.d.ts.map +0 -1
  147. package/dist/hooks/chat-scroll.js.map +0 -1
  148. /package/dist/{hooks → views/Chat}/chat-scroll.d.ts +0 -0
@@ -0,0 +1,139 @@
1
+ import { IconBox, Input } from '@citric/core'
2
+ import { Check, Download, EllipsisHorizontal, Pencil, Trash } from '@citric/icons'
3
+ import { IconButton, LoadingCircular } from '@citric/ui'
4
+ import { aiClient } from '@stack-spot/portal-network'
5
+ import { ConversationResponse } from '@stack-spot/portal-network/api/ai'
6
+ import { theme } from '@stack-spot/portal-theme'
7
+ import { last } from 'lodash'
8
+ import { useCallback, useEffect, useRef, useState } from 'react'
9
+ import { OverlayMenu } from '../../components/OverlayMenu'
10
+ import { useWidget } from '../../context/hooks'
11
+ import { ChatEntry } from '../../state/ChatEntry'
12
+ import { ChatState, MessageInterceptor } from '../../state/ChatState'
13
+ import { ButtonAction } from '../../types'
14
+ import { download } from '../../utils/download'
15
+ import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
16
+ import { useHistoryDictionary } from './dictionary'
17
+ import { HistoryItemBox } from './styled'
18
+ import { findStack, findWorkspace, getAllAgents } from './utils'
19
+
20
+ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse, interceptors: MessageInterceptor[] }) => {
21
+ const t = useHistoryDictionary()
22
+ const [isLoading, setLoading] = useState(false)
23
+ const [isRenaming, setRenaming] = useState(false)
24
+ const [renamed, setRenamed] = useState(item.title)
25
+ const [title, setTitle] = useState(item.title)
26
+ const [isDeleted, setDeleted] = useState(false)
27
+ const renameInput = useRef<HTMLInputElement>(null)
28
+ const widget = useWidget()
29
+
30
+ useEffect(() => {
31
+ if (isRenaming) renameInput.current?.focus()
32
+ }, [isRenaming])
33
+
34
+ const onRename = useCallback(() => {
35
+ setRenaming(true)
36
+ }, [])
37
+
38
+ async function onSubmitRename() {
39
+ const prev = title
40
+ setRenaming(false)
41
+ setTitle(renamed)
42
+ if (!renamed || renamed === item.title) return
43
+ try {
44
+ await aiClient.renameChat.mutate({ conversationId: item.id, conversationUpdateTitleRequest: { title: renamed } })
45
+ aiClient.chats.invalidate()
46
+ } catch (error) {
47
+ // eslint-disable-next-line no-console
48
+ console.error(error)
49
+ setTitle(prev)
50
+ setRenaming(true)
51
+ }
52
+ }
53
+
54
+ const onDownload = useCallback(async () => {
55
+ setLoading(true)
56
+ try {
57
+ const content = await aiClient.downloadChat.mutate({ conversationId: item.id })
58
+ download(`${title}.txt`, content)
59
+ } catch (error) {
60
+ // eslint-disable-next-line no-console
61
+ console.error(error)
62
+ }
63
+ setLoading(false)
64
+ }, [])
65
+
66
+ const onDelete = useCallback(async () => {
67
+ setDeleted(true)
68
+ try {
69
+ await aiClient.deleteChat.mutate({ conversationId: item.id })
70
+ aiClient.chats.invalidate()
71
+ } catch (error) {
72
+ // eslint-disable-next-line no-console
73
+ console.error(error)
74
+ setDeleted(false)
75
+ }
76
+ }, [])
77
+
78
+ const onSelect = useCallback(async () => {
79
+ const tab = widget.chatTabs.getAll().find(c => c.id === item.id)
80
+ if (tab) return widget.chatTabs.select(item.id)
81
+ setLoading(true)
82
+ try {
83
+ const chat = await aiClient.chat.query({ conversationId: item.id })
84
+ const [stack, workspace, agents] = await Promise.all([
85
+ findStack(chat.ai_stack_id),
86
+ findWorkspace(chat.workspace_id),
87
+ getAllAgents(),
88
+ ])
89
+ widget.chatTabs.add(new ChatState({
90
+ id: chat.id,
91
+ initial: { label: chat.title, stack, workspace, agent: agents.find(a => a.id === last(chat.history)?.custom_agent?.id) },
92
+ interceptors,
93
+ entries: chat.history?.map(item => new ChatEntry({
94
+ agentType: item.agent === 'USER' ? 'user' : 'bot',
95
+ content: item.content,
96
+ type: item.agent === 'USER' ? 'text' : 'md',
97
+ agent: agents.find(a => a.id === item.custom_agent?.id),
98
+ messageId: item.message_id,
99
+ knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
100
+ updated: item.updated,
101
+ })),
102
+ }))
103
+ widget.chatTabs.select(chat.id)
104
+ } catch (error) {
105
+ // eslint-disable-next-line no-console
106
+ console.error(error)
107
+ }
108
+ setLoading(false)
109
+ }, [])
110
+
111
+ const actions: ButtonAction[] = [
112
+ { label: t.rename, onClick: onRename, icon: <Pencil /> },
113
+ { label: t.download, onClick: onDownload, icon: <Download /> },
114
+ { label: t.delete, onClick: onDelete, icon: <Trash />, color: theme.color.danger[500] },
115
+ ]
116
+
117
+ return isDeleted ? null : (
118
+ <HistoryItemBox className={isLoading ? 'loading' : ''}>
119
+ {isRenaming ? (
120
+ <>
121
+ <Input
122
+ ref={renameInput}
123
+ value={renamed}
124
+ onChange={e => setRenamed(e.target.value)}
125
+ onKeyDown={e => e.key === 'Enter' && onSubmitRename()}
126
+ />
127
+ <IconButton onClick={onSubmitRename}><Check /></IconButton>
128
+ </>
129
+ ) : (
130
+ <>
131
+ <button className="label" onClick={onSelect} disabled={isLoading}>{title}</button>
132
+ {isLoading ? <LoadingCircular size="xs" /> : <OverlayMenu actions={actions} position="left">
133
+ <IconBox><EllipsisHorizontal /></IconBox>
134
+ </OverlayMenu>}
135
+ </>
136
+ )}
137
+ </HistoryItemBox>
138
+ )
139
+ }
@@ -0,0 +1,20 @@
1
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
2
+
3
+ const dictionary = {
4
+ en: {
5
+ title: 'History',
6
+ description: 'Manage or continue previous conversations.',
7
+ rename: 'Rename',
8
+ download: 'Download',
9
+ delete: 'Delete',
10
+ },
11
+ pt: {
12
+ title: 'Histórico',
13
+ description: 'Gerencie ou retome conversas anteriores.',
14
+ rename: 'Renomear',
15
+ download: 'Download',
16
+ delete: 'Remover',
17
+ },
18
+ } satisfies Dictionary
19
+
20
+ export const useHistoryDictionary = () => useTranslate(dictionary)
@@ -0,0 +1,31 @@
1
+ import { useEffect } from 'react'
2
+ import { FallbackBoundary } from '../../components/FallbackBoundary'
3
+ import { useWidget, useWidgetState } from '../../context/hooks'
4
+ import { useRightPanel } from '../../right-panel/hooks'
5
+ import { MessageInterceptor } from '../../state/ChatState'
6
+ import { ChatHistoryPanel } from './ChatHistoryPanel'
7
+ import { useHistoryDictionary } from './dictionary'
8
+
9
+ export const ChatHistory = ({ interceptors }: { interceptors: MessageInterceptor[] }) => {
10
+ const t = useHistoryDictionary()
11
+ const panel = useWidgetState('panel')
12
+ const { open } = useRightPanel()
13
+ const widget = useWidget()
14
+
15
+ useEffect(() => {
16
+ if (panel === 'history') open(
17
+ <FallbackBoundary><ChatHistoryPanel interceptors={interceptors} /></FallbackBoundary>,
18
+ {
19
+ title: t.title,
20
+ description: t.description,
21
+ onClose: () => widget.set('panel', undefined),
22
+ },
23
+ )
24
+ }, [panel, t])
25
+
26
+ return null
27
+ }
28
+
29
+
30
+
31
+
@@ -0,0 +1,60 @@
1
+ import { IconBox } from '@citric/core'
2
+ import { IconButton } from '@citric/ui'
3
+ import { theme } from '@stack-spot/portal-theme'
4
+ import { styled } from 'styled-components'
5
+
6
+ export const HistoryItemBox = styled.div`
7
+ padding: 8px;
8
+ display: flex;
9
+ flex-direction: row;
10
+ align-items: center;
11
+ border-radius: 4px;
12
+ transition: background-color 0.2s;
13
+ cursor: pointer;
14
+ gap: 20px;
15
+
16
+ &:hover:not(.loading) {
17
+ background-color: ${theme.color.light[600]};
18
+ }
19
+
20
+ &.loading {
21
+ cursor: progress;
22
+
23
+ > svg {
24
+ width: 18px;
25
+ height: 18px;
26
+ }
27
+ }
28
+
29
+ button.label {
30
+ opacity: 0.8;
31
+ background-color: transparent;
32
+ border: none;
33
+ padding: 0;
34
+ color: ${theme.color.light.contrastText};
35
+ flex: 1;
36
+ display: flex;
37
+ cursor: pointer;
38
+ text-align: left;
39
+
40
+ &:disabled {
41
+ opacity: 0.4;
42
+ }
43
+ }
44
+
45
+ ${IconBox}, ${IconButton} {
46
+ padding: 0;
47
+ border: none;
48
+ background-color: transparent;
49
+ width: auto;
50
+ height: auto;
51
+
52
+ svg {
53
+ width: 12px;
54
+ }
55
+ }
56
+
57
+ ${IconButton} svg {
58
+ width: 16px;
59
+ }
60
+ `
@@ -0,0 +1,37 @@
1
+ import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
2
+ import { ChatProperties } from '../../state/ChatState'
3
+
4
+ export async function findStack(id: string | null): Promise<ChatProperties['stack'] | undefined> {
5
+ if (!id) return
6
+ try {
7
+ const stacks = await aiClient.aiStacks.query({})
8
+ return { id, label: stacks.find(s => s.id === id)?.name || id }
9
+ } catch (error) {
10
+ // eslint-disable-next-line no-console
11
+ console.error(error)
12
+ return { id, label: id }
13
+ }
14
+ }
15
+
16
+ export async function findWorkspace(id: string | null): Promise<ChatProperties['workspace'] | undefined> {
17
+ if (!id) return
18
+ try {
19
+ const ws = await workspaceClient.workspace.query({ workspaceId: id })
20
+ return { id, label: ws.name }
21
+ } catch (error) {
22
+ // eslint-disable-next-line no-console
23
+ console.error(error)
24
+ return { id, label: id }
25
+ }
26
+ }
27
+
28
+ export async function getAllAgents(): Promise<Required<ChatProperties>['agent'][]> {
29
+ try {
30
+ const [agents, publicAgents] = await Promise.all([agentClient.agents.query({}), agentClient.publicAgents.query({})])
31
+ return [...agents, ...publicAgents].map(a => ({ id: a.id, label: a.name, image: a.avatar }))
32
+ } catch (error) {
33
+ // eslint-disable-next-line no-console
34
+ console.error(error)
35
+ return []
36
+ }
37
+ }
@@ -35,7 +35,7 @@ export const ChatTabSelection = ({ history, interceptors }: Props) => {
35
35
  label: t.openHistory,
36
36
  className: 'test',
37
37
  style: { marginLeft: 'auto' },
38
- onClick: () => { /* todo */ },
38
+ onClick: () => widget.set('panel', 'history'),
39
39
  })
40
40
  }
41
41
  return actions
@@ -4,12 +4,20 @@ import { Placeholder } from '@stack-spot/portal-components/Placeholder'
4
4
  import { aiClient } from '@stack-spot/portal-network'
5
5
  import { KnowledgeSourceItemResponse, VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
- import { useEffect, useMemo, useState } from 'react'
7
+ import { difference, uniqBy } from 'lodash'
8
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
8
9
  import { DescribedCheckboxGroup } from '../components/form/DescribedCheckboxGroup'
9
10
  import { IconInput } from '../components/IconInput'
10
11
  import { RightPanelTabs } from '../components/RightPanelTabs'
11
12
  import { useCurrentChat, useWidget, useWidgetState } from '../context/hooks'
12
13
  import { useRightPanel } from '../right-panel/hooks'
14
+ import { ChatProperties } from '../state/ChatState'
15
+
16
+ interface TabProps {
17
+ visibility: VisibilityLevelEnum,
18
+ onSubmit: () => void,
19
+ allKS: React.MutableRefObject<ChatProperties['knowledgeSources']>,
20
+ }
13
21
 
14
22
  export const KnowledgeSources = () => {
15
23
  const t = useTranslate(dictionary)
@@ -29,24 +37,33 @@ export const KnowledgeSources = () => {
29
37
 
30
38
  const KnowledgeSourcesPanel = () => {
31
39
  const t = useTranslate(dictionary)
40
+ const chat = useCurrentChat()
41
+ const allKS = useRef(chat.get('knowledgeSources') ?? [])
42
+ const { close } = useRightPanel()
43
+
44
+ const onSubmit = useCallback(() => {
45
+ chat.set('knowledgeSources', allKS.current)
46
+ close()
47
+ }, [chat])
48
+
49
+ useEffect(() => {
50
+ allKS.current = chat.get('knowledgeSources') ?? []
51
+ }, [chat])
32
52
 
33
- return <RightPanelTabs tabs={[
34
- { title: t.personal, content: <KnowledgeSourcesTab key="personal" visibility="personal" /> },
35
- { title: t.shared, content: <KnowledgeSourcesTab key="shared" visibility="shared" /> },
36
- { title: t.account, content: <KnowledgeSourcesTab key="account" visibility="account" /> },
53
+ return <RightPanelTabs key={chat.id} tabs={[
54
+ { title: t.personal, content: <KnowledgeSourcesTab key="personal" visibility="personal" allKS={allKS} onSubmit={onSubmit} /> },
55
+ { title: t.shared, content: <KnowledgeSourcesTab key="shared" visibility="shared" allKS={allKS} onSubmit={onSubmit} /> },
56
+ { title: t.account, content: <KnowledgeSourcesTab key="account" visibility="account" allKS={allKS} onSubmit={onSubmit} /> },
37
57
  ]} />
38
58
  }
39
59
 
40
- const KnowledgeSourcesTab = ({ visibility }: { visibility: VisibilityLevelEnum }) => {
60
+ const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit }: TabProps) => {
41
61
  const t = useTranslate(dictionary)
42
- const { close } = useRightPanel()
43
- const chat = useCurrentChat()
44
62
  const [filter, setFilter] = useState('')
45
63
  const knowledgeSources = aiClient.knowledgeSources.useQuery({ visibility, order: 'a-to-z' })
46
- const [hasChanged, setChanged] = useState(false)
47
64
  const [value, setValue] = useState<KnowledgeSourceItemResponse[]>((() => {
48
- const currentlySelected = chat.get('knowledgeSources')?.map(ks => ks.id)
49
- return knowledgeSources.filter(ks => currentlySelected?.includes(ks.id))
65
+ const currentlySelected = allKS.current?.map(ks => ks.id)
66
+ return knowledgeSources.filter(ks => currentlySelected?.includes(ks.slug))
50
67
  })())
51
68
  const filtered = useMemo(
52
69
  () => filter
@@ -55,10 +72,15 @@ const KnowledgeSourcesTab = ({ visibility }: { visibility: VisibilityLevelEnum }
55
72
  [knowledgeSources, filter, value],
56
73
  )
57
74
 
58
- function submit() {
59
- if (value) chat.set('knowledgeSources', value.map(({ id, name }) => ({ id, label: name })))
60
- close()
61
- }
75
+ const onChange = useCallback((newValue: KnowledgeSourceItemResponse[]) => {
76
+ setValue((current) => {
77
+ const added = difference(newValue, current)
78
+ const removed = difference(current, newValue)
79
+ allKS.current = allKS.current?.filter(ks => !removed.some(r => r.slug === ks.id)) ?? []
80
+ allKS.current = uniqBy([...allKS.current, ...added.map(ks => ({ id: ks.slug, label: ks.name }))], 'id')
81
+ return newValue
82
+ })
83
+ }, [])
62
84
 
63
85
  return (
64
86
  <>
@@ -68,10 +90,7 @@ const KnowledgeSourcesTab = ({ visibility }: { visibility: VisibilityLevelEnum }
68
90
  options={filtered}
69
91
  keygen={ks => ks.id}
70
92
  value={value}
71
- onChange={(value) => {
72
- setValue(value)
73
- setChanged(true)
74
- }}
93
+ onChange={onChange}
75
94
  renderLabel={ks => ks.name}
76
95
  renderDescription={ks => ks.description}
77
96
  optionClassName={ks => (filter && !ks.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()) && value.includes(ks))
@@ -85,7 +104,7 @@ const KnowledgeSourcesTab = ({ visibility }: { visibility: VisibilityLevelEnum }
85
104
  )}
86
105
  {!knowledgeSources.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
87
106
  </div>
88
- <Button onClick={submit} disabled={!hasChanged}>{t.apply}</Button>
107
+ <Button onClick={onSubmit}>{t.apply}</Button>
89
108
  </>
90
109
  )
91
110
  }
@@ -29,8 +29,9 @@ export const Stacks = () => {
29
29
 
30
30
  const StacksPanel = () => {
31
31
  const t = useTranslate(dictionary)
32
+ const chat = useCurrentChat()
32
33
 
33
- return <RightPanelTabs tabs={[
34
+ return <RightPanelTabs key={chat.id} tabs={[
34
35
  { title: t.personal, content: <StacksTab key="personal" visibility="personal" /> },
35
36
  { title: t.shared, content: <StacksTab key="shared" visibility="shared" /> },
36
37
  { title: t.account, content: <StacksTab key="account" visibility="account" /> },
@@ -16,13 +16,14 @@ export const Workspaces = () => {
16
16
  const panel = useWidgetState('panel')
17
17
  const { open } = useRightPanel()
18
18
  const widget = useWidget()
19
+ const chat = useCurrentChat()
19
20
 
20
21
  useEffect(() => {
21
22
  if (panel === 'workspace') open(
22
- <RightPanelForm><WorkspacesPanel /></RightPanelForm>,
23
+ <RightPanelForm><WorkspacesPanel key={chat.id} /></RightPanelForm>,
23
24
  { title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
24
25
  )
25
- }, [panel, t])
26
+ }, [panel, t, chat.id])
26
27
 
27
28
  return null
28
29
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-scroll.d.ts","sourceRoot":"","sources":["../../src/hooks/chat-scroll.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAMzF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-scroll.js","sourceRoot":"","sources":["../../src/hooks/chat-scroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAiC,EAAE,IAAW;IACxF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;QAClD,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAA;IACpC,CAAC,EAAE,IAAI,CAAC,CAAA;AACV,CAAC"}
File without changes