@stack-spot/ai-chat-widget 1.1.1 → 1.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.
- package/CHANGELOG.md +16 -0
- package/dist/StackspotAIWidget.d.ts +1 -12
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +5 -18
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +1 -1
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +21 -2
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/AutoFocus.d.ts.map +1 -1
- package/dist/components/AutoFocus.js +8 -1
- package/dist/components/AutoFocus.js.map +1 -1
- package/dist/components/FadingOverflow.js +2 -2
- package/dist/components/FadingOverflow.js.map +1 -1
- package/dist/components/QuickStartButton.d.ts +6 -1
- package/dist/components/QuickStartButton.d.ts.map +1 -1
- package/dist/components/QuickStartButton.js +6 -2
- package/dist/components/QuickStartButton.js.map +1 -1
- package/dist/components/RightPanelForm.d.ts.map +1 -1
- package/dist/components/RightPanelForm.js +2 -1
- package/dist/components/RightPanelForm.js.map +1 -1
- package/dist/context/hooks.d.ts +2 -7
- package/dist/context/hooks.d.ts.map +1 -1
- package/dist/context/hooks.js +4 -16
- package/dist/context/hooks.js.map +1 -1
- package/dist/features.d.ts +16 -17
- package/dist/features.d.ts.map +1 -1
- package/dist/features.js +17 -9
- package/dist/features.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout.css +7 -0
- package/dist/state/ChatState.d.ts +23 -3
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js +5 -2
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/ChatTabsController.d.ts +21 -3
- package/dist/state/ChatTabsController.d.ts.map +1 -1
- package/dist/state/ChatTabsController.js +49 -11
- package/dist/state/ChatTabsController.js.map +1 -1
- package/dist/state/WidgetState.d.ts +50 -1
- package/dist/state/WidgetState.d.ts.map +1 -1
- package/dist/state/WidgetState.js +55 -2
- package/dist/state/WidgetState.js.map +1 -1
- package/dist/utils/chat.d.ts +1 -8
- package/dist/utils/chat.d.ts.map +1 -1
- package/dist/utils/chat.js +0 -15
- package/dist/utils/chat.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts +9 -0
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -0
- package/dist/views/Agents/AgentDescription.js +21 -0
- package/dist/views/Agents/AgentDescription.js.map +1 -0
- package/dist/views/Agents/AgentsPanel.d.ts +5 -0
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -0
- package/dist/views/Agents/AgentsPanel.js +19 -0
- package/dist/views/Agents/AgentsPanel.js.map +1 -0
- package/dist/views/Agents/AgentsTab.d.ts +5 -0
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -0
- package/dist/views/Agents/AgentsTab.js +43 -0
- package/dist/views/Agents/AgentsTab.js.map +1 -0
- package/dist/views/Agents/dictionary.d.ts +2 -0
- package/dist/views/Agents/dictionary.d.ts.map +1 -0
- package/dist/views/Agents/dictionary.js +35 -0
- package/dist/views/Agents/dictionary.js.map +1 -0
- package/dist/views/Agents/index.d.ts +5 -0
- package/dist/views/Agents/index.d.ts.map +1 -0
- package/dist/views/Agents/index.js +21 -0
- package/dist/views/Agents/index.js.map +1 -0
- package/dist/views/Agents/styled.d.ts +3 -0
- package/dist/views/Agents/styled.d.ts.map +1 -0
- package/dist/views/Agents/styled.js +58 -0
- package/dist/views/Agents/styled.js.map +1 -0
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +1 -1
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/index.js +1 -1
- package/dist/views/Chat/index.js.map +1 -1
- package/dist/views/Chat/styled.js +1 -1
- package/dist/views/ChatHistory/ChatHistoryPanel.d.ts +1 -4
- package/dist/views/ChatHistory/ChatHistoryPanel.d.ts.map +1 -1
- package/dist/views/ChatHistory/ChatHistoryPanel.js +2 -2
- package/dist/views/ChatHistory/ChatHistoryPanel.js.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.d.ts +1 -3
- package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.js +2 -2
- package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
- package/dist/views/ChatHistory/index.d.ts +1 -9
- package/dist/views/ChatHistory/index.d.ts.map +1 -1
- package/dist/views/ChatHistory/index.js +2 -2
- package/dist/views/ChatHistory/index.js.map +1 -1
- package/dist/views/ChatTabSelection.d.ts +1 -7
- package/dist/views/ChatTabSelection.d.ts.map +1 -1
- package/dist/views/ChatTabSelection.js +7 -7
- package/dist/views/ChatTabSelection.js.map +1 -1
- package/dist/views/Editor.d.ts.map +1 -1
- package/dist/views/Editor.js +4 -1
- package/dist/views/Editor.js.map +1 -1
- package/dist/views/Home/BuiltInAgent.d.ts +6 -0
- package/dist/views/Home/BuiltInAgent.d.ts.map +1 -0
- package/dist/views/{Home.js → Home/BuiltInAgent.js} +7 -41
- package/dist/views/Home/BuiltInAgent.js.map +1 -0
- package/dist/views/Home/CustomAgent.d.ts +5 -0
- package/dist/views/Home/CustomAgent.d.ts.map +1 -0
- package/dist/views/Home/CustomAgent.js +24 -0
- package/dist/views/Home/CustomAgent.js.map +1 -0
- package/dist/views/Home/index.d.ts +8 -0
- package/dist/views/Home/index.d.ts.map +1 -0
- package/dist/views/Home/index.js +15 -0
- package/dist/views/Home/index.js.map +1 -0
- package/dist/views/Home/styled.d.ts +2 -0
- package/dist/views/Home/styled.d.ts.map +1 -0
- package/dist/views/Home/styled.js +59 -0
- package/dist/views/Home/styled.js.map +1 -0
- package/dist/views/Home/types.d.ts +7 -0
- package/dist/views/Home/types.d.ts.map +1 -0
- package/dist/views/Home/types.js +2 -0
- package/dist/views/Home/types.js.map +1 -0
- package/dist/views/KnowledgeSources.js +1 -1
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts +1 -6
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.js +12 -4
- package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
- package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
- package/dist/views/MessageInput/InfoBar.js +16 -6
- package/dist/views/MessageInput/InfoBar.js.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +2 -1
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/index.d.ts +1 -9
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +2 -2
- 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 +6 -2
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/MinimizedHeader.d.ts.map +1 -1
- package/dist/views/MinimizedHeader.js +2 -3
- package/dist/views/MinimizedHeader.js.map +1 -1
- package/dist/views/Stacks.js +2 -1
- package/dist/views/Stacks.js.map +1 -1
- package/dist/views/Workspaces.js +2 -1
- package/dist/views/Workspaces.js.map +1 -1
- package/package.json +2 -2
- package/src/StackspotAIWidget.tsx +6 -32
- package/src/chat-interceptors/quick-commands.ts +1 -1
- package/src/chat-interceptors/send-message.ts +22 -2
- package/src/components/AutoFocus.tsx +9 -1
- package/src/components/FadingOverflow.tsx +2 -2
- package/src/components/QuickStartButton.tsx +17 -5
- package/src/components/RightPanelForm.tsx +2 -1
- package/src/context/hooks.ts +8 -20
- package/src/features.ts +27 -24
- package/src/index.ts +6 -0
- package/src/layout.css +7 -0
- package/src/state/ChatState.ts +26 -4
- package/src/state/ChatTabsController.ts +50 -11
- package/src/state/WidgetState.ts +88 -2
- package/src/utils/chat.ts +1 -18
- package/src/views/Agents/AgentDescription.tsx +48 -0
- package/src/views/Agents/AgentsPanel.tsx +19 -0
- package/src/views/Agents/AgentsTab.tsx +80 -0
- package/src/views/Agents/dictionary.ts +36 -0
- package/src/views/Agents/index.tsx +26 -0
- package/src/views/Agents/styled.ts +59 -0
- package/src/views/Chat/ChatMessage.tsx +19 -17
- package/src/views/Chat/index.tsx +1 -1
- package/src/views/Chat/styled.ts +1 -1
- package/src/views/ChatHistory/ChatHistoryPanel.tsx +2 -3
- package/src/views/ChatHistory/HistoryItem.tsx +3 -3
- package/src/views/ChatHistory/index.tsx +2 -10
- package/src/views/ChatTabSelection.tsx +8 -13
- package/src/views/Editor.tsx +4 -1
- package/src/views/{Home.tsx → Home/BuiltInAgent.tsx} +7 -48
- package/src/views/Home/CustomAgent.tsx +39 -0
- package/src/views/Home/index.tsx +20 -0
- package/src/views/Home/styled.ts +59 -0
- package/src/views/Home/types.ts +6 -0
- package/src/views/KnowledgeSources.tsx +2 -2
- package/src/views/MessageInput/ButtonGroup.tsx +15 -12
- package/src/views/MessageInput/InfoBar.tsx +25 -9
- package/src/views/MessageInput/QuickCommandSelector.tsx +2 -1
- package/src/views/MessageInput/index.tsx +1 -10
- package/src/views/MessageInput/styled.ts +6 -2
- package/src/views/MinimizedHeader.tsx +2 -3
- package/src/views/Stacks.tsx +3 -2
- package/src/views/Workspaces.tsx +3 -2
- package/dist/views/Agents.d.ts +0 -2
- package/dist/views/Agents.d.ts.map +0 -1
- package/dist/views/Agents.js +0 -146
- package/dist/views/Agents.js.map +0 -1
- package/dist/views/Home.d.ts +0 -14
- package/dist/views/Home.d.ts.map +0 -1
- package/dist/views/Home.js.map +0 -1
- package/src/views/Agents.tsx +0 -203
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { last, pull } from 'lodash'
|
|
1
|
+
import { groupBy, last, pull } from 'lodash'
|
|
2
2
|
import { ChatState } from './ChatState'
|
|
3
3
|
|
|
4
|
-
type TabChangeListener = (chats: ChatState[],
|
|
4
|
+
type TabChangeListener = (chats: ChatState[], active: ChatState) => void
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Controls the chat tabs.
|
|
@@ -12,7 +12,7 @@ export class ChatTabsController {
|
|
|
12
12
|
private listeners: TabChangeListener[] = []
|
|
13
13
|
|
|
14
14
|
private runListeners() {
|
|
15
|
-
this.listeners.forEach(l => l(this.chats, this.
|
|
15
|
+
this.listeners.forEach(l => l(this.chats, this.getActiveChat()))
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -26,20 +26,50 @@ export class ChatTabsController {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* If the active chat is deleted, this selects a new chat based on the position of the chat that has been removed.
|
|
30
|
+
*
|
|
31
|
+
* @param previous the previous tabs.
|
|
32
|
+
* @param deleted the ids of the chats that have been deleted.
|
|
31
33
|
*/
|
|
32
|
-
|
|
33
|
-
if (this.
|
|
34
|
-
|
|
35
|
-
this.chats = this.chats.filter(c => !ids.includes(c.id))
|
|
36
|
-
if (ids.includes(this.activeChatId)) {
|
|
34
|
+
private reselect(previous: ChatState[], deleted: string[]) {
|
|
35
|
+
if (deleted.includes(this.activeChatId)) {
|
|
36
|
+
const currentActiveIndex = previous.findIndex(c => c.id === this.activeChatId)
|
|
37
37
|
if (currentActiveIndex === -1) this.activeChatId = this.chats[0]?.id
|
|
38
38
|
this.activeChatId = currentActiveIndex < this.chats.length ? this.chats[currentActiveIndex].id : last(this.chats)?.id ?? ''
|
|
39
39
|
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Removes chats from the tab view. This will never remove a chat if this chat is the last one.
|
|
44
|
+
*
|
|
45
|
+
* @param ids the ids of the chats to remove.
|
|
46
|
+
*/
|
|
47
|
+
remove(...ids: string[]) {
|
|
48
|
+
ids.splice(this.chats.length - 1, ids.length - this.chats.length)
|
|
49
|
+
if (!ids.length) return
|
|
50
|
+
const previous = this.chats
|
|
51
|
+
this.chats = this.chats.filter(c => !ids.includes(c.id))
|
|
52
|
+
this.reselect(previous, ids)
|
|
40
53
|
this.runListeners()
|
|
41
54
|
}
|
|
42
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Removes all empty chats. The last chat will be kept if it's empty, but the only chat remaining.
|
|
58
|
+
*
|
|
59
|
+
* @param exceptions ids of chats that shouldn't be removed even if empty.
|
|
60
|
+
*/
|
|
61
|
+
removeEmptyChats(...exceptions: string[]) {
|
|
62
|
+
const lastChat = last(this.chats)
|
|
63
|
+
const previous = this.chats
|
|
64
|
+
const { true: removed, false: kept = [] } = groupBy(this.chats, c => c.getMessages().length === 0 && !exceptions.includes(c.id))
|
|
65
|
+
if (removed) {
|
|
66
|
+
this.chats = kept
|
|
67
|
+
if (lastChat && this.chats.length === 0) this.chats.push(lastChat)
|
|
68
|
+
this.reselect(previous, removed.map(c => c.id))
|
|
69
|
+
this.runListeners()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
43
73
|
/**
|
|
44
74
|
* @param id the id of the chat to retrieve.
|
|
45
75
|
* @returns a ChatState corresponding to the id.
|
|
@@ -49,12 +79,21 @@ export class ChatTabsController {
|
|
|
49
79
|
}
|
|
50
80
|
|
|
51
81
|
/**
|
|
52
|
-
* @returns the
|
|
82
|
+
* @returns the id of the chat corresponding to the tab that is currently active.
|
|
53
83
|
*/
|
|
54
84
|
getActiveChatId() {
|
|
55
85
|
return this.activeChatId
|
|
56
86
|
}
|
|
57
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @returns the chat corresponding to the tab that is currently active.
|
|
90
|
+
*/
|
|
91
|
+
getActiveChat() {
|
|
92
|
+
const chat = this.chats.find(c => c.id === this.activeChatId)
|
|
93
|
+
if (!chat) throw new Error('Chat state error, the active chat id corresponds to none chat in the tabs.')
|
|
94
|
+
return chat
|
|
95
|
+
}
|
|
96
|
+
|
|
58
97
|
/**
|
|
59
98
|
* @returns all chats in tab view.
|
|
60
99
|
*/
|
package/src/state/WidgetState.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { loader } from '@monaco-editor/react'
|
|
2
|
+
import { ulid } from 'ulid'
|
|
3
|
+
import { quickCommandQuestionsInterceptor } from '../chat-interceptors/quick-command-questions'
|
|
4
|
+
import { createQuickCommandInterceptor } from '../chat-interceptors/quick-commands'
|
|
5
|
+
import { sendMessageInterceptor } from '../chat-interceptors/send-message'
|
|
6
|
+
import { AIWidgetFeatures, ChatFeatures, getFeaturesWithDefaults, GlobalFeatures } from '../features'
|
|
7
|
+
import { ChatEntry } from './ChatEntry'
|
|
8
|
+
import { ChatPropertiesWithOptionalFeatures, ChatState, MessageInterceptor } from './ChatState'
|
|
1
9
|
import { ChatTabsController } from './ChatTabsController'
|
|
2
10
|
import { ObservableState } from './ObservableState'
|
|
3
11
|
|
|
@@ -14,8 +22,40 @@ export interface WidgetProperties {
|
|
|
14
22
|
* Whether or not the widget is in its minimized version.
|
|
15
23
|
*/
|
|
16
24
|
isMinimized?: boolean,
|
|
25
|
+
/**
|
|
26
|
+
* Global features of the chat widget. These features don't depend on the chat currently selected.
|
|
27
|
+
*/
|
|
28
|
+
features: GlobalFeatures,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface Options {
|
|
32
|
+
/**
|
|
33
|
+
* Chat interceptors allows you to intercept chat messages, interpret them, modify them and even change the chat flow. See
|
|
34
|
+
* {@link ChatState} for more details.
|
|
35
|
+
*
|
|
36
|
+
* These interceptors, in addition to the default interceptors, will be used when creating a new chat via `WidgetState#createChat`.
|
|
37
|
+
*/
|
|
38
|
+
interceptors?: MessageInterceptor[],
|
|
39
|
+
/**
|
|
40
|
+
* Features to enable in the widget. If a feature is not set explicitly to false, it's interpreted as enabled.
|
|
41
|
+
*
|
|
42
|
+
* These features will be used when creating a new chat via `WidgetState#createChat`. Updating chat features only influence new chats.
|
|
43
|
+
*/
|
|
44
|
+
features?: Partial<AIWidgetFeatures>,
|
|
45
|
+
/**
|
|
46
|
+
* The initial values for the widget state.
|
|
47
|
+
*
|
|
48
|
+
* The default value for each feature under `initial.features` is true.
|
|
49
|
+
*/
|
|
50
|
+
initial?: Omit<WidgetProperties, 'features'>,
|
|
51
|
+
/**
|
|
52
|
+
* The initial tabs for the chat window.
|
|
53
|
+
*/
|
|
54
|
+
chatTabs?: ChatTabsController,
|
|
17
55
|
}
|
|
18
56
|
|
|
57
|
+
const untitledChatPrefix = 'Chat '
|
|
58
|
+
|
|
19
59
|
/**
|
|
20
60
|
* Holds the full state of the AI Chat Widget.
|
|
21
61
|
*
|
|
@@ -23,9 +63,55 @@ export interface WidgetProperties {
|
|
|
23
63
|
*/
|
|
24
64
|
export class WidgetState extends ObservableState<WidgetProperties> {
|
|
25
65
|
readonly chatTabs: ChatTabsController
|
|
66
|
+
/**
|
|
67
|
+
* Chat interceptors to be used by default when creating a new chat.
|
|
68
|
+
*/
|
|
69
|
+
interceptors: MessageInterceptor[] = []
|
|
70
|
+
/**
|
|
71
|
+
* Chat features to be used by default when creating a new chat.
|
|
72
|
+
*/
|
|
73
|
+
chatFeatures: ChatFeatures
|
|
26
74
|
|
|
27
|
-
constructor(initial:
|
|
28
|
-
|
|
75
|
+
constructor({ chatTabs, initial, interceptors = [], features }: Options = {}) {
|
|
76
|
+
const featuresWithDefaults = getFeaturesWithDefaults(features)
|
|
77
|
+
super({ ...initial, features: featuresWithDefaults })
|
|
78
|
+
this.chatFeatures = featuresWithDefaults
|
|
29
79
|
this.chatTabs = chatTabs ?? new ChatTabsController()
|
|
80
|
+
this.interceptors = [
|
|
81
|
+
...interceptors,
|
|
82
|
+
quickCommandQuestionsInterceptor,
|
|
83
|
+
createQuickCommandInterceptor(this, () => loader.__getMonacoInstance()?.editor),
|
|
84
|
+
sendMessageInterceptor,
|
|
85
|
+
]
|
|
86
|
+
this.createChat()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private getNextUntitledChatIndex() {
|
|
90
|
+
let max = 0
|
|
91
|
+
for (const chat of this.chatTabs.getAll()) {
|
|
92
|
+
const [, match] = chat.get('label').match(`${untitledChatPrefix}(\\d+)`) ?? []
|
|
93
|
+
const index = parseInt(match)
|
|
94
|
+
if (index > max) max = index
|
|
95
|
+
}
|
|
96
|
+
return max + 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Utility function for adding a new chat to the chat tabs. This also selects the new tab.
|
|
101
|
+
* @param properties the chat properties (initial state).
|
|
102
|
+
* @param entries the entries for the chat to start with.
|
|
103
|
+
* @returns the chat created.
|
|
104
|
+
*/
|
|
105
|
+
createChat({ label, ...properties }: Partial<ChatPropertiesWithOptionalFeatures> = {}, entries: ChatEntry[] = []) {
|
|
106
|
+
const chat = new ChatState({
|
|
107
|
+
id: ulid(),
|
|
108
|
+
initial: { label: label || `${untitledChatPrefix}${this.getNextUntitledChatIndex()}`, features: this.chatFeatures, ...properties },
|
|
109
|
+
entries,
|
|
110
|
+
interceptors: this.interceptors,
|
|
111
|
+
untitled: !label,
|
|
112
|
+
})
|
|
113
|
+
this.chatTabs.add(chat)
|
|
114
|
+
this.chatTabs.select(chat.id)
|
|
115
|
+
return chat
|
|
30
116
|
}
|
|
31
117
|
}
|
package/src/utils/chat.ts
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
import { FixedChatRequest } from '@stack-spot/portal-network'
|
|
2
|
-
import { ulid } from 'ulid'
|
|
3
2
|
import appData from '../app-metadata.json'
|
|
4
|
-
import { ChatState
|
|
5
|
-
import { WidgetState } from '../state/WidgetState'
|
|
3
|
+
import { ChatState } from '../state/ChatState'
|
|
6
4
|
import { defaultLanguage } from './programming-languages'
|
|
7
5
|
|
|
8
|
-
// helps with naming new chats
|
|
9
|
-
let next = 1
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Utility for creating a new chat in the chat tabs.
|
|
13
|
-
* @param widget the widget state.
|
|
14
|
-
* @param interceptors the interceptors to add the chat.
|
|
15
|
-
*/
|
|
16
|
-
export function createNewChat(widget: WidgetState, interceptors: MessageInterceptor[]) {
|
|
17
|
-
const id = ulid()
|
|
18
|
-
widget.chatTabs.add(new ChatState({ id, initial: { label: `Chat ${next}` }, interceptors }))
|
|
19
|
-
widget.chatTabs.select(id)
|
|
20
|
-
next++
|
|
21
|
-
}
|
|
22
|
-
|
|
23
6
|
/**
|
|
24
7
|
* Builds a conversation context from a ChatState.
|
|
25
8
|
*
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Text } from '@citric/core'
|
|
2
|
+
import { Badge, Skeleton } from '@citric/ui'
|
|
3
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { useAgentsDictionary } from './dictionary'
|
|
6
|
+
import { AgentDescriptionBox } from './styled'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
agentId?: string,
|
|
10
|
+
llm?: string,
|
|
11
|
+
description?: string,
|
|
12
|
+
numberOfKnowledgeSources: number,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeSources }: Props) => {
|
|
16
|
+
const t = useAgentsDictionary()
|
|
17
|
+
const [agent,,, { isLoading }] = agentClient.agent.useStatefulQuery({ agentId: agentId! }, { enabled: !!agentId })
|
|
18
|
+
const knowledgeSources = useMemo(
|
|
19
|
+
() => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
20
|
+
<li key={index}><Badge palette="teal" appearance="square">{ks.name}</Badge></li>
|
|
21
|
+
)),
|
|
22
|
+
[agent],
|
|
23
|
+
)
|
|
24
|
+
const skeleton = useMemo(() => {
|
|
25
|
+
const loadingKS: React.ReactElement[] = []
|
|
26
|
+
for (let i = 0; i < numberOfKnowledgeSources; i++) {
|
|
27
|
+
loadingKS.push(<li key={i}><Badge palette="teal" appearance="square"><Skeleton className="ks-skeleton" /></Badge></li>)
|
|
28
|
+
}
|
|
29
|
+
return loadingKS
|
|
30
|
+
}, [numberOfKnowledgeSources])
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<AgentDescriptionBox>
|
|
34
|
+
{description && <section>
|
|
35
|
+
<Text appearance="microtext1" className="title">{t.description}</Text>
|
|
36
|
+
<Text>{description}</Text>
|
|
37
|
+
</section>}
|
|
38
|
+
{(!!numberOfKnowledgeSources || !!knowledgeSources?.length) && <section>
|
|
39
|
+
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
40
|
+
<ul>{isLoading ? skeleton : knowledgeSources}</ul>
|
|
41
|
+
</section>}
|
|
42
|
+
{llm && <section>
|
|
43
|
+
<Text appearance="microtext1" className="title">LLM</Text>
|
|
44
|
+
<Badge palette="orange" appearance="square">{llm}</Badge>
|
|
45
|
+
</section>}
|
|
46
|
+
</AgentDescriptionBox>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RightPanelTabs } from '../../components/RightPanelTabs'
|
|
2
|
+
import { useCurrentChat } from '../../context/hooks'
|
|
3
|
+
import { AgentsTab } from './AgentsTab'
|
|
4
|
+
import { useAgentsDictionary } from './dictionary'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Renders the Agent selection form in the Right Panel if this is the panel that is currently opened.
|
|
8
|
+
*/
|
|
9
|
+
export const AgentsPanel = () => {
|
|
10
|
+
const t = useAgentsDictionary()
|
|
11
|
+
const chat = useCurrentChat()
|
|
12
|
+
|
|
13
|
+
return <RightPanelTabs key={chat.id} tabs={[
|
|
14
|
+
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
|
|
15
|
+
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
16
|
+
{ title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" /> },
|
|
17
|
+
{ title: t.account, content: <AgentsTab key="account" visibility="ACCOUNT" /> },
|
|
18
|
+
]} />
|
|
19
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Button, Text } from '@citric/core'
|
|
2
|
+
import { Search } from '@citric/icons'
|
|
3
|
+
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
4
|
+
import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
5
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
6
|
+
import { AgentResponse, VisibilityLevel } from '@stack-spot/portal-network/api/agent'
|
|
7
|
+
import { useMemo, useState } from 'react'
|
|
8
|
+
import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
|
|
9
|
+
import { IconInput } from '../../components/IconInput'
|
|
10
|
+
import { useCurrentChat } from '../../context/hooks'
|
|
11
|
+
import { useRightPanel } from '../../right-panel/hooks'
|
|
12
|
+
import { AgentDescription } from './AgentDescription'
|
|
13
|
+
import { useAgentsDictionary } from './dictionary'
|
|
14
|
+
import { AgentLabel } from './styled'
|
|
15
|
+
|
|
16
|
+
export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' }) => {
|
|
17
|
+
const t = useAgentsDictionary()
|
|
18
|
+
const { close } = useRightPanel()
|
|
19
|
+
const chat = useCurrentChat()
|
|
20
|
+
const [filter, setFilter] = useState('')
|
|
21
|
+
const defaultAgent = useMemo(() => ({
|
|
22
|
+
id: '',
|
|
23
|
+
name: 'Stackspot AI',
|
|
24
|
+
description: t.defaultAgentDescription,
|
|
25
|
+
llm_config: { model_slug: 'gpt4o' },
|
|
26
|
+
} as AgentResponse), [])
|
|
27
|
+
const agents = visibility === 'BUILT-IN' ? agentClient.publicAgents.useQuery({}) : agentClient.agents.useQuery({ visibility })
|
|
28
|
+
const [value, setValue] = useState<AgentResponse | undefined>(
|
|
29
|
+
chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : defaultAgent,
|
|
30
|
+
)
|
|
31
|
+
const filtered = useMemo(
|
|
32
|
+
() => {
|
|
33
|
+
const ags = visibility === 'BUILT-IN' ? [defaultAgent as AgentResponse, ...agents] : agents
|
|
34
|
+
return filter ? ags.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : ags
|
|
35
|
+
},
|
|
36
|
+
[agents, filter, value],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
function submit() {
|
|
40
|
+
if (value) {
|
|
41
|
+
chat.set('agent', value.id ? { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN' } : undefined)
|
|
42
|
+
}
|
|
43
|
+
close()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<div className="content">
|
|
49
|
+
<IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
|
|
50
|
+
{!!filtered.length && <DescribedRadioGroup
|
|
51
|
+
options={filtered}
|
|
52
|
+
keygen={a => a.id}
|
|
53
|
+
value={value}
|
|
54
|
+
onChange={setValue}
|
|
55
|
+
renderLabel={({ name, avatar, id }) => (
|
|
56
|
+
<AgentLabel>
|
|
57
|
+
{id ? (avatar && <img src={avatar} />) : <MiniLogo />}
|
|
58
|
+
<Text>{name}</Text>
|
|
59
|
+
</AgentLabel>
|
|
60
|
+
)}
|
|
61
|
+
renderDescription={a => <AgentDescription
|
|
62
|
+
agentId={a.id}
|
|
63
|
+
description={a.description}
|
|
64
|
+
llm={a.llm_config?.model_slug}
|
|
65
|
+
numberOfKnowledgeSources={a.knowledge_sources_config?.knowledge_sources?.length ?? 0}
|
|
66
|
+
/>}
|
|
67
|
+
optionClassName={a => (a === value && filter && !a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
68
|
+
? 'filtered-out'
|
|
69
|
+
: ''
|
|
70
|
+
}
|
|
71
|
+
className="option-list"
|
|
72
|
+
/>}
|
|
73
|
+
{!!agents.length && !filtered.length &&
|
|
74
|
+
<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder" />}
|
|
75
|
+
{!agents.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
|
|
76
|
+
</div>
|
|
77
|
+
{!!filtered.length && <Button onClick={submit} disabled={!value}>{t.apply}</Button>}
|
|
78
|
+
</>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
+
|
|
3
|
+
const dictionary = {
|
|
4
|
+
en: {
|
|
5
|
+
title: 'Agents',
|
|
6
|
+
subtitle: 'By selecting an Agent, it will be consulted to generate the answers.',
|
|
7
|
+
personal: 'Personal',
|
|
8
|
+
builtin: 'Built-in',
|
|
9
|
+
shared: 'Shared',
|
|
10
|
+
account: 'Account',
|
|
11
|
+
apply: 'Apply',
|
|
12
|
+
noSearchResults: "Your search didn't yield results.",
|
|
13
|
+
noSearchResultsDescription: 'Please, try another search term.',
|
|
14
|
+
noData: 'There are no agents in this category yet.',
|
|
15
|
+
noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
|
|
16
|
+
defaultAgentDescription: 'The StackSpot CodeGen is an advanced artificial intelligence agent designed to optimize and accelerate software development. Integrated directly into your integrated development environment, StackSpot CodeGen offers real-time code suggestions, helping developers write high-quality code more efficiently. With robust features such as creating Stacks AI, customized knowledge sources, and quick commands, StackSpot CodeGen contextualizes your development needs to provide the best answers and code suggestions.',
|
|
17
|
+
description: 'Description',
|
|
18
|
+
},
|
|
19
|
+
pt: {
|
|
20
|
+
title: 'Agentes',
|
|
21
|
+
subtitle: 'Ao selecionar um Agente, ele será consultado para gerar as respostas.',
|
|
22
|
+
personal: 'Pessoal',
|
|
23
|
+
builtin: 'Embutido',
|
|
24
|
+
shared: 'Compartilhado',
|
|
25
|
+
account: 'Conta',
|
|
26
|
+
apply: 'Aplicar',
|
|
27
|
+
noSearchResults: 'Sua busca não produziu resultados.',
|
|
28
|
+
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
29
|
+
noData: 'Ainda não há agentes nesta categoria.',
|
|
30
|
+
noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
|
|
31
|
+
defaultAgentDescription: 'O StackSpot CodeGen é um agente de inteligência artificial avançado projetado para otimizar e acelerar o desenvolvimento de software. Integrado diretamente ao seu ambiente de desenvolvimento, o StackSpot CodeGen oferece sugestões de código em tempo real, ajudando os desenvolvedores a escreverem código de alta qualidade de forma mais eficiente. Com recursos robustos, como a criação de Stacks AI, Knowledge Sources personalizadas e comandos rápidos, o StackSpot CodeGen contextualiza suas necessidades de desenvolvimento para fornecer as melhores respostas e sugestões de código.',
|
|
32
|
+
description: 'Descrição',
|
|
33
|
+
},
|
|
34
|
+
} satisfies Dictionary
|
|
35
|
+
|
|
36
|
+
export const useAgentsDictionary = () => useTranslate(dictionary)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useWidget, useWidgetState } from '../../context/hooks'
|
|
3
|
+
import { useRightPanel } from '../../right-panel/hooks'
|
|
4
|
+
import { AgentsPanel } from './AgentsPanel'
|
|
5
|
+
import { useAgentsDictionary } from './dictionary'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Renders the panel to select an agent of this panel is opened.
|
|
9
|
+
*/
|
|
10
|
+
export const Agents = () => {
|
|
11
|
+
const t = useAgentsDictionary()
|
|
12
|
+
const panel = useWidgetState('panel')
|
|
13
|
+
const { open } = useRightPanel()
|
|
14
|
+
const widget = useWidget()
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (panel === 'agent') open(
|
|
18
|
+
<AgentsPanel />,
|
|
19
|
+
{ title: t.title, description: t.subtitle, onClose: () => widget.set('panel', undefined) },
|
|
20
|
+
)
|
|
21
|
+
}, [panel, t])
|
|
22
|
+
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
2
|
+
import { styled } from 'styled-components'
|
|
3
|
+
|
|
4
|
+
export const AgentLabel = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: row;
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: 6px;
|
|
9
|
+
|
|
10
|
+
img, svg {
|
|
11
|
+
width: 20px;
|
|
12
|
+
height: 20px;
|
|
13
|
+
border-radius: 50%;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
export const AgentDescriptionBox = styled.div`
|
|
19
|
+
color: ${theme.color.light[700]};
|
|
20
|
+
line-height: 18px;
|
|
21
|
+
|
|
22
|
+
section {
|
|
23
|
+
border-bottom: 1px solid ${theme.color.light[600]};
|
|
24
|
+
padding-bottom: 10px;
|
|
25
|
+
margin-bottom: 10px;
|
|
26
|
+
|
|
27
|
+
&:last-child {
|
|
28
|
+
padding-bottom: 0;
|
|
29
|
+
margin-bottom: 0;
|
|
30
|
+
border-bottom: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.title {
|
|
34
|
+
display: block;
|
|
35
|
+
margin-bottom: 6px;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ul {
|
|
40
|
+
padding: 0;
|
|
41
|
+
margin: 0;
|
|
42
|
+
list-style: none;
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: row;
|
|
45
|
+
flex-wrap: wrap;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
gap: 6px;
|
|
48
|
+
|
|
49
|
+
li {
|
|
50
|
+
margin: 3px 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ks-skeleton {
|
|
55
|
+
width: 100px;
|
|
56
|
+
height: 12px;
|
|
57
|
+
mix-blend-mode: color-dodge;
|
|
58
|
+
}
|
|
59
|
+
`
|
|
@@ -86,21 +86,23 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
|
|
|
86
86
|
? <Markdown onCopyCode={(code) => onCopyCode(code, entry.messageId ?? '', chat)}>{entry.content}</Markdown>
|
|
87
87
|
: <p className="plain-text">{entry.content}</p>
|
|
88
88
|
}
|
|
89
|
-
{entry.actions?.length &&
|
|
90
|
-
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
{entry.actions?.length && (
|
|
90
|
+
<div className="actions">
|
|
91
|
+
{entry.actions.map(
|
|
92
|
+
(a, index) => (
|
|
93
|
+
<Button
|
|
94
|
+
key={index}
|
|
95
|
+
appearance={a.appearance === 'primary' ? 'contained' : 'outlined'}
|
|
96
|
+
colorScheme="inverse"
|
|
97
|
+
onClick={() => runAction(a)}
|
|
98
|
+
disabled={!isLast}
|
|
99
|
+
>
|
|
100
|
+
{a.title}
|
|
101
|
+
</Button>
|
|
102
|
+
),
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
104
106
|
</div>}
|
|
105
107
|
</div>
|
|
106
108
|
{entry.error && (
|
|
@@ -111,8 +113,8 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
|
|
|
111
113
|
)}
|
|
112
114
|
{!!entry.knowledgeSources?.length && <div className="ks-box">
|
|
113
115
|
<Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
|
|
114
|
-
<ul>{entry.knowledgeSources.map(ks => (
|
|
115
|
-
<li key={
|
|
116
|
+
<ul>{entry.knowledgeSources.map((ks, index) => (
|
|
117
|
+
<li key={index}>
|
|
116
118
|
<Button size="sm" colorScheme="light" onClick={() => detailKS(ks)}>{ks.name}</Button>
|
|
117
119
|
</li>
|
|
118
120
|
))}</ul>
|
package/src/views/Chat/index.tsx
CHANGED
|
@@ -13,5 +13,5 @@ interface Props {
|
|
|
13
13
|
*/
|
|
14
14
|
export const Chat = ({ username }: Props) => {
|
|
15
15
|
const { active } = useChatTabs()
|
|
16
|
-
return <ChatMessages key={active} chatId={active} username={username} />
|
|
16
|
+
return <ChatMessages key={active.id} chatId={active.id} username={username} />
|
|
17
17
|
}
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -132,12 +132,12 @@ export const ChatList = styled.ul`
|
|
|
132
132
|
|
|
133
133
|
&.user {
|
|
134
134
|
align-items: end;
|
|
135
|
+
margin-left: 15%;
|
|
135
136
|
|
|
136
137
|
.chat-message {
|
|
137
138
|
display: flex;
|
|
138
139
|
flex-direction: row;
|
|
139
140
|
gap: 8px;
|
|
140
|
-
align-items: center;
|
|
141
141
|
|
|
142
142
|
.message-content {
|
|
143
143
|
padding: 10px;
|
|
@@ -2,13 +2,12 @@ import { aiClient } from '@stack-spot/portal-network'
|
|
|
2
2
|
import InfiniteScroll from 'react-infinite-scroll-component'
|
|
3
3
|
import { AutoFocus } from '../../components/AutoFocus'
|
|
4
4
|
import { HistoryList } from '../../components/HistoryList'
|
|
5
|
-
import { MessageInterceptor } from '../../state/ChatState'
|
|
6
5
|
import { HistoryItem } from './HistoryItem'
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Renders the list of conversations (history).
|
|
10
9
|
*/
|
|
11
|
-
export const ChatHistoryPanel = (
|
|
10
|
+
export const ChatHistoryPanel = () => {
|
|
12
11
|
const [chats, { fetchNextPage, hasNextPage }] = aiClient.chats.useInfiniteQuery({ size: 40 })
|
|
13
12
|
return (
|
|
14
13
|
<AutoFocus id="chatHistoryList" style={{ height: '100%', overflow: 'auto' }}>
|
|
@@ -23,7 +22,7 @@ export const ChatHistoryPanel = ({ interceptors }: { interceptors: MessageInterc
|
|
|
23
22
|
items={chats}
|
|
24
23
|
getDate={c => new Date(c.updated || c.created || '')}
|
|
25
24
|
keygen={c => c.id}
|
|
26
|
-
renderItem={c => <HistoryItem item={c}
|
|
25
|
+
renderItem={c => <HistoryItem item={c} />}
|
|
27
26
|
style={{ marginRight: '6px' }}
|
|
28
27
|
/>
|
|
29
28
|
</InfiniteScroll>
|
|
@@ -10,7 +10,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
10
10
|
import { OverlayMenu } from '../../components/OverlayMenu'
|
|
11
11
|
import { useWidget } from '../../context/hooks'
|
|
12
12
|
import { ChatEntry } from '../../state/ChatEntry'
|
|
13
|
-
import { ChatState
|
|
13
|
+
import { ChatState } from '../../state/ChatState'
|
|
14
14
|
import { ButtonAction } from '../../types'
|
|
15
15
|
import { download } from '../../utils/download'
|
|
16
16
|
import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
|
|
@@ -21,7 +21,7 @@ import { findStack, findWorkspace, getAllAgents } from './utils'
|
|
|
21
21
|
/**
|
|
22
22
|
* Renders an item of the list of conversations (history).
|
|
23
23
|
*/
|
|
24
|
-
export const HistoryItem = ({ item
|
|
24
|
+
export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
|
|
25
25
|
const t = useHistoryDictionary()
|
|
26
26
|
const [isLoading, setLoading] = useState(false)
|
|
27
27
|
const [isRenaming, setRenaming] = useState(false)
|
|
@@ -98,7 +98,7 @@ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse
|
|
|
98
98
|
widget.chatTabs.add(new ChatState({
|
|
99
99
|
id: chat.id,
|
|
100
100
|
initial: { label: chat.title, stack, workspace, agent: agent ? { ...agent, builtIn } : undefined },
|
|
101
|
-
interceptors,
|
|
101
|
+
interceptors: widget.interceptors,
|
|
102
102
|
entries: chat.history?.map(item => new ChatEntry({
|
|
103
103
|
agentType: item.agent === 'USER' ? 'user' : 'bot',
|
|
104
104
|
content: item.content,
|
|
@@ -2,21 +2,13 @@ import { useEffect } from 'react'
|
|
|
2
2
|
import { FallbackBoundary } from '../../components/FallbackBoundary'
|
|
3
3
|
import { useWidget, useWidgetState } from '../../context/hooks'
|
|
4
4
|
import { useRightPanel } from '../../right-panel/hooks'
|
|
5
|
-
import { MessageInterceptor } from '../../state/ChatState'
|
|
6
5
|
import { ChatHistoryPanel } from './ChatHistoryPanel'
|
|
7
6
|
import { useHistoryDictionary } from './dictionary'
|
|
8
7
|
|
|
9
|
-
interface Props {
|
|
10
|
-
/**
|
|
11
|
-
* The chat interceptors to use when recreating a ChatState from the history.
|
|
12
|
-
*/
|
|
13
|
-
interceptors: MessageInterceptor[],
|
|
14
|
-
}
|
|
15
|
-
|
|
16
8
|
/**
|
|
17
9
|
* Renders the Chat History in the Right Panel if this is the panel that is currently opened.
|
|
18
10
|
*/
|
|
19
|
-
export const ChatHistory = (
|
|
11
|
+
export const ChatHistory = () => {
|
|
20
12
|
const t = useHistoryDictionary()
|
|
21
13
|
const panel = useWidgetState('panel')
|
|
22
14
|
const { open } = useRightPanel()
|
|
@@ -24,7 +16,7 @@ export const ChatHistory = ({ interceptors }: Props) => {
|
|
|
24
16
|
|
|
25
17
|
useEffect(() => {
|
|
26
18
|
if (panel === 'history') open(
|
|
27
|
-
<FallbackBoundary><ChatHistoryPanel
|
|
19
|
+
<FallbackBoundary><ChatHistoryPanel /></FallbackBoundary>,
|
|
28
20
|
{
|
|
29
21
|
title: t.title,
|
|
30
22
|
description: t.description,
|