@stack-spot/ai-chat-widget 0.2.0 → 0.3.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 (117) 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/send-message.d.ts.map +1 -1
  5. package/dist/chat-interceptors/send-message.js +2 -6
  6. package/dist/chat-interceptors/send-message.js.map +1 -1
  7. package/dist/components/HistoryList.d.ts +2 -5
  8. package/dist/components/HistoryList.d.ts.map +1 -1
  9. package/dist/components/HistoryList.js +70 -2
  10. package/dist/components/HistoryList.js.map +1 -1
  11. package/dist/components/OverlayMenu.d.ts +3 -2
  12. package/dist/components/OverlayMenu.d.ts.map +1 -1
  13. package/dist/components/OverlayMenu.js +57 -1
  14. package/dist/components/OverlayMenu.js.map +1 -1
  15. package/dist/components/Tooltip/Tooltip.d.ts +2 -1
  16. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  17. package/dist/components/Tooltip/Tooltip.js +10 -2
  18. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  19. package/dist/components/Tooltip/TooltipAPI.d.ts +3 -2
  20. package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -1
  21. package/dist/components/Tooltip/TooltipAPI.js +26 -1
  22. package/dist/components/Tooltip/TooltipAPI.js.map +1 -1
  23. package/dist/components/Tooltip/style.d.ts.map +1 -1
  24. package/dist/components/Tooltip/style.js +0 -1
  25. package/dist/components/Tooltip/style.js.map +1 -1
  26. package/dist/components/Tooltip/types.d.ts +6 -0
  27. package/dist/components/Tooltip/types.d.ts.map +1 -1
  28. package/dist/features.d.ts.map +1 -1
  29. package/dist/features.js +1 -0
  30. package/dist/features.js.map +1 -1
  31. package/dist/types.d.ts +1 -1
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/utils/date.d.ts +1 -0
  34. package/dist/utils/date.d.ts.map +1 -1
  35. package/dist/utils/date.js +3 -0
  36. package/dist/utils/date.js.map +1 -1
  37. package/dist/utils/download.d.ts +2 -0
  38. package/dist/utils/download.d.ts.map +1 -0
  39. package/dist/utils/download.js +10 -0
  40. package/dist/utils/download.js.map +1 -0
  41. package/dist/utils/knowledge-source.d.ts +3 -1
  42. package/dist/utils/knowledge-source.d.ts.map +1 -1
  43. package/dist/utils/knowledge-source.js +8 -0
  44. package/dist/utils/knowledge-source.js.map +1 -1
  45. package/dist/views/Agents.js +2 -1
  46. package/dist/views/Agents.js.map +1 -1
  47. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  48. package/dist/views/Chat/ChatMessage.js +1 -1
  49. package/dist/views/Chat/ChatMessage.js.map +1 -1
  50. package/dist/views/Chat/chat-scroll.d.ts.map +1 -0
  51. package/dist/{hooks → views/Chat}/chat-scroll.js +5 -3
  52. package/dist/views/Chat/chat-scroll.js.map +1 -0
  53. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts +5 -0
  54. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts.map +1 -0
  55. package/dist/views/ChatHistory/ChatHistoryPanel.js +10 -0
  56. package/dist/views/ChatHistory/ChatHistoryPanel.js.map +1 -0
  57. package/dist/views/ChatHistory/HistoryItem.d.ts +7 -0
  58. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -0
  59. package/dist/views/ChatHistory/HistoryItem.js +109 -0
  60. package/dist/views/ChatHistory/HistoryItem.js.map +1 -0
  61. package/dist/views/ChatHistory/dictionary.d.ts +2 -0
  62. package/dist/views/ChatHistory/dictionary.d.ts.map +1 -0
  63. package/dist/views/ChatHistory/dictionary.js +19 -0
  64. package/dist/views/ChatHistory/dictionary.js.map +1 -0
  65. package/dist/views/ChatHistory/index.d.ts +5 -0
  66. package/dist/views/ChatHistory/index.d.ts.map +1 -0
  67. package/dist/views/ChatHistory/index.js +23 -0
  68. package/dist/views/ChatHistory/index.js.map +1 -0
  69. package/dist/views/ChatHistory/styled.d.ts +2 -0
  70. package/dist/views/ChatHistory/styled.d.ts.map +1 -0
  71. package/dist/views/ChatHistory/styled.js +60 -0
  72. package/dist/views/ChatHistory/styled.js.map +1 -0
  73. package/dist/views/ChatHistory/utils.d.ts +4 -0
  74. package/dist/views/ChatHistory/utils.d.ts.map +1 -0
  75. package/dist/views/ChatHistory/utils.js +28 -0
  76. package/dist/views/ChatHistory/utils.js.map +1 -0
  77. package/dist/views/ChatTabSelection.js +1 -1
  78. package/dist/views/ChatTabSelection.js.map +1 -1
  79. package/dist/views/KnowledgeSources.d.ts.map +1 -1
  80. package/dist/views/KnowledgeSources.js +30 -21
  81. package/dist/views/KnowledgeSources.js.map +1 -1
  82. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  83. package/dist/views/Stacks.js +2 -1
  84. package/dist/views/Stacks.js.map +1 -1
  85. package/dist/views/Workspaces.d.ts.map +1 -1
  86. package/dist/views/Workspaces.js +3 -2
  87. package/dist/views/Workspaces.js.map +1 -1
  88. package/package.json +3 -2
  89. package/src/StackspotAIWidget.tsx +2 -0
  90. package/src/chat-interceptors/send-message.ts +2 -6
  91. package/src/components/HistoryList.tsx +80 -7
  92. package/src/components/OverlayMenu.tsx +70 -3
  93. package/src/components/Tooltip/Tooltip.tsx +13 -7
  94. package/src/components/Tooltip/TooltipAPI.ts +22 -2
  95. package/src/components/Tooltip/style.tsx +0 -1
  96. package/src/components/Tooltip/types.ts +7 -0
  97. package/src/features.ts +1 -0
  98. package/src/types.ts +1 -1
  99. package/src/utils/date.ts +4 -0
  100. package/src/utils/download.ts +12 -0
  101. package/src/utils/knowledge-source.ts +13 -1
  102. package/src/views/Agents.tsx +2 -1
  103. package/src/views/Chat/ChatMessage.tsx +1 -1
  104. package/src/{hooks → views/Chat}/chat-scroll.ts +6 -3
  105. package/src/views/ChatHistory/ChatHistoryPanel.tsx +28 -0
  106. package/src/views/ChatHistory/HistoryItem.tsx +127 -0
  107. package/src/views/ChatHistory/dictionary.ts +20 -0
  108. package/src/views/ChatHistory/index.tsx +31 -0
  109. package/src/views/ChatHistory/styled.ts +60 -0
  110. package/src/views/ChatHistory/utils.ts +26 -0
  111. package/src/views/ChatTabSelection.tsx +1 -1
  112. package/src/views/KnowledgeSources.tsx +39 -20
  113. package/src/views/Stacks.tsx +2 -1
  114. package/src/views/Workspaces.tsx +3 -2
  115. package/dist/hooks/chat-scroll.d.ts.map +0 -1
  116. package/dist/hooks/chat-scroll.js.map +0 -1
  117. /package/dist/{hooks → views/Chat}/chat-scroll.d.ts +0 -0
@@ -0,0 +1,127 @@
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 { useCallback, useEffect, useRef, useState } from 'react'
8
+ import { OverlayMenu } from '../../components/OverlayMenu'
9
+ import { useWidget } from '../../context/hooks'
10
+ import { ChatEntry } from '../../state/ChatEntry'
11
+ import { ChatState, MessageInterceptor } from '../../state/ChatState'
12
+ import { ButtonAction } from '../../types'
13
+ import { download } from '../../utils/download'
14
+ import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
15
+ import { useHistoryDictionary } from './dictionary'
16
+ import { HistoryItemBox } from './styled'
17
+ import { findStack, findWorkspace } from './utils'
18
+
19
+ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse, interceptors: MessageInterceptor[] }) => {
20
+ const t = useHistoryDictionary()
21
+ const [isLoading, setLoading] = useState(false)
22
+ const [isRenaming, setRenaming] = useState(false)
23
+ const [renamed, setRenamed] = useState(item.title)
24
+ const [title, setTitle] = useState(item.title)
25
+ const [isDeleted, setDeleted] = useState(false)
26
+ const renameInput = useRef<HTMLInputElement>(null)
27
+ const widget = useWidget()
28
+
29
+ useEffect(() => {
30
+ if (isRenaming) renameInput.current?.focus()
31
+ }, [isRenaming])
32
+
33
+ const onRename = useCallback(() => {
34
+ setRenaming(true)
35
+ }, [])
36
+
37
+ async function onSubmitRename() {
38
+ setRenaming(false)
39
+ if (!renamed || renamed === item.title) return
40
+ try {
41
+ await aiClient.renameChat.mutate({ conversationId: item.id, conversationUpdateTitleRequest: { title: renamed } })
42
+ setTitle(renamed)
43
+ aiClient.chats.invalidate()
44
+ } catch (error) {
45
+ // eslint-disable-next-line no-console
46
+ console.error(error)
47
+ setRenaming(true)
48
+ }
49
+ }
50
+
51
+ const onDownload = useCallback(async () => {
52
+ setLoading(true)
53
+ try {
54
+ const content = await aiClient.downloadChat.mutate({ conversationId: item.id })
55
+ download(`${title}.txt`, content)
56
+ } catch (error) {
57
+ // eslint-disable-next-line no-console
58
+ console.error(error)
59
+ }
60
+ setLoading(false)
61
+ }, [])
62
+
63
+ const onDelete = useCallback(async () => {
64
+ setDeleted(true)
65
+ try {
66
+ await aiClient.deleteChat.mutate({ conversationId: item.id })
67
+ aiClient.chats.invalidate()
68
+ } catch (error) {
69
+ // eslint-disable-next-line no-console
70
+ console.error(error)
71
+ setDeleted(false)
72
+ }
73
+ }, [])
74
+
75
+ const onSelect = useCallback(async () => {
76
+ const tab = widget.chatTabs.getAll().find(c => c.id === item.id)
77
+ if (tab) return widget.chatTabs.select(item.id)
78
+ setLoading(true)
79
+ try {
80
+ const chat = await aiClient.chat.query({ conversationId: item.id })
81
+ const [stack, workspace] = await Promise.all([findStack(chat.ai_stack_id), findWorkspace(chat.workspace_id)])
82
+ widget.chatTabs.add(new ChatState({
83
+ id: chat.id,
84
+ initial: { label: chat.title, stack, workspace },
85
+ interceptors,
86
+ entries: chat.history?.map(item => new ChatEntry({
87
+ agent: item.agent === 'USER' ? 'user' : 'bot',
88
+ content: item.content,
89
+ type: item.agent === 'USER' ? 'text' : 'md',
90
+ agentId: item.custom_agent?.id,
91
+ messageId: item.message_id,
92
+ knowledgeSources: genericSourcesToKnowledgeSources(item.sources),
93
+ updated: item.updated,
94
+ })),
95
+ }))
96
+ widget.chatTabs.select(chat.id)
97
+ } catch (error) {
98
+ // eslint-disable-next-line no-console
99
+ console.error(error)
100
+ }
101
+ setLoading(false)
102
+ }, [])
103
+
104
+ const actions: ButtonAction[] = [
105
+ { label: t.rename, onClick: onRename, icon: <Pencil /> },
106
+ { label: t.download, onClick: onDownload, icon: <Download /> },
107
+ { label: t.delete, onClick: onDelete, icon: <Trash />, color: theme.color.danger[500] },
108
+ ]
109
+
110
+ return isDeleted ? null : (
111
+ <HistoryItemBox className={isLoading ? 'loading' : ''}>
112
+ {isRenaming ? (
113
+ <>
114
+ <Input ref={renameInput} value={renamed} onChange={e => setRenamed(e.target.value)} />
115
+ <IconButton onClick={onSubmitRename}><Check /></IconButton>
116
+ </>
117
+ ) : (
118
+ <>
119
+ <button className="label" onClick={onSelect} disabled={isLoading}>{title}</button>
120
+ {isLoading ? <LoadingCircular size="xs" /> : <OverlayMenu actions={actions} position="left">
121
+ <IconBox><EllipsisHorizontal /></IconBox>
122
+ </OverlayMenu>}
123
+ </>
124
+ )}
125
+ </HistoryItemBox>
126
+ )
127
+ }
@@ -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,26 @@
1
+ import { 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
+ }
@@ -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