@stack-spot/ai-chat-widget 0.1.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/dist/StackspotAIWidget.d.ts +13 -0
- package/dist/StackspotAIWidget.d.ts.map +1 -0
- package/dist/StackspotAIWidget.js +32 -0
- package/dist/StackspotAIWidget.js.map +1 -0
- package/dist/chat-interceptors/quick-commands.d.ts +4 -0
- package/dist/chat-interceptors/quick-commands.d.ts.map +1 -0
- package/dist/chat-interceptors/quick-commands.js +11 -0
- package/dist/chat-interceptors/quick-commands.js.map +1 -0
- package/dist/chat-interceptors/send-message.d.ts +4 -0
- package/dist/chat-interceptors/send-message.d.ts.map +1 -0
- package/dist/chat-interceptors/send-message.js +36 -0
- package/dist/chat-interceptors/send-message.js.map +1 -0
- package/dist/components/Accordion.d.ts +10 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +46 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/AdaptiveTextArea.d.ts +10 -0
- package/dist/components/AdaptiveTextArea.d.ts.map +1 -0
- package/dist/components/AdaptiveTextArea.js +28 -0
- package/dist/components/AdaptiveTextArea.js.map +1 -0
- package/dist/components/Code.d.ts +19 -0
- package/dist/components/Code.d.ts.map +1 -0
- package/dist/components/Code.js +112 -0
- package/dist/components/Code.js.map +1 -0
- package/dist/components/Editor.d.ts +9 -0
- package/dist/components/Editor.d.ts.map +1 -0
- package/dist/components/Editor.js +2 -0
- package/dist/components/Editor.js.map +1 -0
- package/dist/components/FadingOverflow.d.ts +12 -0
- package/dist/components/FadingOverflow.d.ts.map +1 -0
- package/dist/components/FadingOverflow.js +216 -0
- package/dist/components/FadingOverflow.js.map +1 -0
- package/dist/components/FallbackBoundary/ErrorBoundary.d.ts +30 -0
- package/dist/components/FallbackBoundary/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/FallbackBoundary/ErrorBoundary.js +38 -0
- package/dist/components/FallbackBoundary/ErrorBoundary.js.map +1 -0
- package/dist/components/FallbackBoundary/Loading.d.ts +2 -0
- package/dist/components/FallbackBoundary/Loading.d.ts.map +1 -0
- package/dist/components/FallbackBoundary/Loading.js +12 -0
- package/dist/components/FallbackBoundary/Loading.js.map +1 -0
- package/dist/components/FallbackBoundary/index.d.ts +6 -0
- package/dist/components/FallbackBoundary/index.d.ts.map +1 -0
- package/dist/components/FallbackBoundary/index.js +9 -0
- package/dist/components/FallbackBoundary/index.js.map +1 -0
- package/dist/components/HistoryList.d.ts +13 -0
- package/dist/components/HistoryList.d.ts.map +1 -0
- package/dist/components/HistoryList.js +4 -0
- package/dist/components/HistoryList.js.map +1 -0
- package/dist/components/IconInput.d.ts +7 -0
- package/dist/components/IconInput.d.ts.map +1 -0
- package/dist/components/IconInput.js +58 -0
- package/dist/components/IconInput.js.map +1 -0
- package/dist/components/Markdown.d.ts +9 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/Markdown.js +17 -0
- package/dist/components/Markdown.js.map +1 -0
- package/dist/components/OverlayMenu.d.ts +9 -0
- package/dist/components/OverlayMenu.d.ts.map +1 -0
- package/dist/components/OverlayMenu.js +2 -0
- package/dist/components/OverlayMenu.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +11 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +126 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/QuickStartButton.d.ts +8 -0
- package/dist/components/QuickStartButton.d.ts.map +1 -0
- package/dist/components/QuickStartButton.js +40 -0
- package/dist/components/QuickStartButton.js.map +1 -0
- package/dist/components/RightPanelForm.d.ts +5 -0
- package/dist/components/RightPanelForm.d.ts.map +1 -0
- package/dist/components/RightPanelForm.js +45 -0
- package/dist/components/RightPanelForm.js.map +1 -0
- package/dist/components/RightPanelTabs.d.ts +10 -0
- package/dist/components/RightPanelTabs.d.ts.map +1 -0
- package/dist/components/RightPanelTabs.js +20 -0
- package/dist/components/RightPanelTabs.js.map +1 -0
- package/dist/components/TabManager.d.ts +34 -0
- package/dist/components/TabManager.d.ts.map +1 -0
- package/dist/components/TabManager.js +158 -0
- package/dist/components/TabManager.js.map +1 -0
- package/dist/components/Tooltip/Tooltip.d.ts +11 -0
- package/dist/components/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip/Tooltip.js +9 -0
- package/dist/components/Tooltip/Tooltip.js.map +1 -0
- package/dist/components/Tooltip/TooltipAPI.d.ts +10 -0
- package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -0
- package/dist/components/Tooltip/TooltipAPI.js +48 -0
- package/dist/components/Tooltip/TooltipAPI.js.map +1 -0
- package/dist/components/Tooltip/context.d.ts +5 -0
- package/dist/components/Tooltip/context.d.ts.map +1 -0
- package/dist/components/Tooltip/context.js +18 -0
- package/dist/components/Tooltip/context.js.map +1 -0
- package/dist/components/Tooltip/index.d.ts +3 -0
- package/dist/components/Tooltip/index.d.ts.map +1 -0
- package/dist/components/Tooltip/index.js +3 -0
- package/dist/components/Tooltip/index.js.map +1 -0
- package/dist/components/Tooltip/style.d.ts +4 -0
- package/dist/components/Tooltip/style.d.ts.map +1 -0
- package/dist/components/Tooltip/style.js +23 -0
- package/dist/components/Tooltip/style.js.map +1 -0
- package/dist/components/Tooltip/types.d.ts +8 -0
- package/dist/components/Tooltip/types.d.ts.map +1 -0
- package/dist/components/Tooltip/types.js +2 -0
- package/dist/components/Tooltip/types.js.map +1 -0
- package/dist/components/form/DescribedCheckboxGroup.d.ts +3 -0
- package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -0
- package/dist/components/form/DescribedCheckboxGroup.js +23 -0
- package/dist/components/form/DescribedCheckboxGroup.js.map +1 -0
- package/dist/components/form/DescribedRadioGroup.d.ts +3 -0
- package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -0
- package/dist/components/form/DescribedRadioGroup.js +18 -0
- package/dist/components/form/DescribedRadioGroup.js.map +1 -0
- package/dist/components/form/styled.d.ts +2 -0
- package/dist/components/form/styled.d.ts.map +1 -0
- package/dist/components/form/styled.js +41 -0
- package/dist/components/form/styled.js.map +1 -0
- package/dist/components/form/types.d.ts +19 -0
- package/dist/components/form/types.d.ts.map +1 -0
- package/dist/components/form/types.js +2 -0
- package/dist/components/form/types.js.map +1 -0
- package/dist/context/AIWidgetProvider.d.ts +4 -0
- package/dist/context/AIWidgetProvider.d.ts.map +1 -0
- package/dist/context/AIWidgetProvider.js +4 -0
- package/dist/context/AIWidgetProvider.js.map +1 -0
- package/dist/context/hooks.d.ts +18 -0
- package/dist/context/hooks.d.ts.map +1 -0
- package/dist/context/hooks.js +82 -0
- package/dist/context/hooks.js.map +1 -0
- package/dist/features.d.ts +13 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/features.js +6 -0
- package/dist/features.js.map +1 -0
- package/dist/hooks/chat-scroll.d.ts +7 -0
- package/dist/hooks/chat-scroll.d.ts.map +1 -0
- package/dist/hooks/chat-scroll.js +15 -0
- package/dist/hooks/chat-scroll.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.css +119 -0
- package/dist/right-panel/DefaultPanel.d.ts +9 -0
- package/dist/right-panel/DefaultPanel.d.ts.map +1 -0
- package/dist/right-panel/DefaultPanel.js +46 -0
- package/dist/right-panel/DefaultPanel.js.map +1 -0
- package/dist/right-panel/RightPanel.d.ts +2 -0
- package/dist/right-panel/RightPanel.d.ts.map +1 -0
- package/dist/right-panel/RightPanel.js +6 -0
- package/dist/right-panel/RightPanel.js.map +1 -0
- package/dist/right-panel/RightPanelProvider.d.ts +13 -0
- package/dist/right-panel/RightPanelProvider.d.ts.map +1 -0
- package/dist/right-panel/RightPanelProvider.js +10 -0
- package/dist/right-panel/RightPanelProvider.js.map +1 -0
- package/dist/right-panel/hooks.d.ts +13 -0
- package/dist/right-panel/hooks.d.ts.map +1 -0
- package/dist/right-panel/hooks.js +39 -0
- package/dist/right-panel/hooks.js.map +1 -0
- package/dist/state/ChatEntry.d.ts +48 -0
- package/dist/state/ChatEntry.d.ts.map +1 -0
- package/dist/state/ChatEntry.js +55 -0
- package/dist/state/ChatEntry.js.map +1 -0
- package/dist/state/ChatState.d.ts +46 -0
- package/dist/state/ChatState.d.ts.map +1 -0
- package/dist/state/ChatState.js +53 -0
- package/dist/state/ChatState.js.map +1 -0
- package/dist/state/ChatTabsController.d.ts +17 -0
- package/dist/state/ChatTabsController.d.ts.map +1 -0
- package/dist/state/ChatTabsController.js +47 -0
- package/dist/state/ChatTabsController.js.map +1 -0
- package/dist/state/ObservableState.d.ts +10 -0
- package/dist/state/ObservableState.d.ts.map +1 -0
- package/dist/state/ObservableState.js +25 -0
- package/dist/state/ObservableState.js.map +1 -0
- package/dist/state/WidgetState.d.ts +19 -0
- package/dist/state/WidgetState.d.ts.map +1 -0
- package/dist/state/WidgetState.js +29 -0
- package/dist/state/WidgetState.js.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/chat.d.ts +6 -0
- package/dist/utils/chat.d.ts.map +1 -0
- package/dist/utils/chat.js +26 -0
- package/dist/utils/chat.js.map +1 -0
- package/dist/utils/date.d.ts +6 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +38 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/views/Agents.d.ts +2 -0
- package/dist/views/Agents.d.ts.map +1 -0
- package/dist/views/Agents.js +2 -0
- package/dist/views/Agents.js.map +1 -0
- package/dist/views/Chat/AgentInfo.d.ts +6 -0
- package/dist/views/Chat/AgentInfo.d.ts.map +1 -0
- package/dist/views/Chat/AgentInfo.js +7 -0
- package/dist/views/Chat/AgentInfo.js.map +1 -0
- package/dist/views/Chat/ChatMessage.d.ts +6 -0
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -0
- package/dist/views/Chat/ChatMessage.js +61 -0
- package/dist/views/Chat/ChatMessage.js.map +1 -0
- package/dist/views/Chat/ChatMessages.d.ts +7 -0
- package/dist/views/Chat/ChatMessages.d.ts.map +1 -0
- package/dist/views/Chat/ChatMessages.js +12 -0
- package/dist/views/Chat/ChatMessages.js.map +1 -0
- package/dist/views/Chat/index.d.ts +6 -0
- package/dist/views/Chat/index.d.ts.map +1 -0
- package/dist/views/Chat/index.js +8 -0
- package/dist/views/Chat/index.js.map +1 -0
- package/dist/views/Chat/styled.d.ts +2 -0
- package/dist/views/Chat/styled.d.ts.map +1 -0
- package/dist/views/Chat/styled.js +116 -0
- package/dist/views/Chat/styled.js.map +1 -0
- package/dist/views/ChatTabSelection.d.ts +8 -0
- package/dist/views/ChatTabSelection.d.ts.map +1 -0
- package/dist/views/ChatTabSelection.js +45 -0
- package/dist/views/ChatTabSelection.js.map +1 -0
- package/dist/views/Editor.d.ts +2 -0
- package/dist/views/Editor.d.ts.map +1 -0
- package/dist/views/Editor.js +2 -0
- package/dist/views/Editor.js.map +1 -0
- package/dist/views/Home.d.ts +6 -0
- package/dist/views/Home.d.ts.map +1 -0
- package/dist/views/Home.js +68 -0
- package/dist/views/Home.js.map +1 -0
- package/dist/views/KnowledgeSources.d.ts +2 -0
- package/dist/views/KnowledgeSources.d.ts.map +1 -0
- package/dist/views/KnowledgeSources.js +82 -0
- package/dist/views/KnowledgeSources.js.map +1 -0
- package/dist/views/MessageInput/ButtonGroup.d.ts +12 -0
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -0
- package/dist/views/MessageInput/ButtonGroup.js +22 -0
- package/dist/views/MessageInput/ButtonGroup.js.map +1 -0
- package/dist/views/MessageInput/InfoBar.d.ts +2 -0
- package/dist/views/MessageInput/InfoBar.d.ts.map +1 -0
- package/dist/views/MessageInput/InfoBar.js +28 -0
- package/dist/views/MessageInput/InfoBar.js.map +1 -0
- package/dist/views/MessageInput/dictionary.d.ts +2 -0
- package/dist/views/MessageInput/dictionary.d.ts.map +1 -0
- package/dist/views/MessageInput/dictionary.js +35 -0
- package/dist/views/MessageInput/dictionary.js.map +1 -0
- package/dist/views/MessageInput/index.d.ts +7 -0
- package/dist/views/MessageInput/index.d.ts.map +1 -0
- package/dist/views/MessageInput/index.js +43 -0
- package/dist/views/MessageInput/index.js.map +1 -0
- package/dist/views/MessageInput/styled.d.ts +2 -0
- package/dist/views/MessageInput/styled.d.ts.map +1 -0
- package/dist/views/MessageInput/styled.js +195 -0
- package/dist/views/MessageInput/styled.js.map +1 -0
- package/dist/views/MinimizedHeader.d.ts +3 -0
- package/dist/views/MinimizedHeader.d.ts.map +1 -0
- package/dist/views/MinimizedHeader.js +76 -0
- package/dist/views/MinimizedHeader.js.map +1 -0
- package/dist/views/Stacks.d.ts +2 -0
- package/dist/views/Stacks.d.ts.map +1 -0
- package/dist/views/Stacks.js +73 -0
- package/dist/views/Stacks.js.map +1 -0
- package/dist/views/Workspaces.d.ts +2 -0
- package/dist/views/Workspaces.d.ts.map +1 -0
- package/dist/views/Workspaces.js +59 -0
- package/dist/views/Workspaces.js.map +1 -0
- package/package.json +52 -0
- package/src/StackspotAIWidget.tsx +65 -0
- package/src/chat-interceptors/quick-commands.ts +12 -0
- package/src/chat-interceptors/send-message.ts +35 -0
- package/src/components/Accordion.tsx +64 -0
- package/src/components/AdaptiveTextArea.tsx +34 -0
- package/src/components/Code.tsx +201 -0
- package/src/components/Editor.tsx +12 -0
- package/src/components/FadingOverflow.tsx +234 -0
- package/src/components/FallbackBoundary/ErrorBoundary.tsx +48 -0
- package/src/components/FallbackBoundary/Loading.tsx +14 -0
- package/src/components/FallbackBoundary/index.tsx +15 -0
- package/src/components/HistoryList.tsx +16 -0
- package/src/components/IconInput.tsx +70 -0
- package/src/components/Markdown.tsx +53 -0
- package/src/components/OverlayMenu.tsx +10 -0
- package/src/components/ProgressBar.tsx +153 -0
- package/src/components/QuickStartButton.tsx +51 -0
- package/src/components/RightPanelForm.tsx +55 -0
- package/src/components/RightPanelTabs.tsx +39 -0
- package/src/components/TabManager.tsx +223 -0
- package/src/components/Tooltip/Tooltip.tsx +30 -0
- package/src/components/Tooltip/TooltipAPI.ts +46 -0
- package/src/components/Tooltip/context.tsx +24 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/Tooltip/style.tsx +25 -0
- package/src/components/Tooltip/types.ts +8 -0
- package/src/components/form/DescribedCheckboxGroup.tsx +39 -0
- package/src/components/form/DescribedRadioGroup.tsx +33 -0
- package/src/components/form/styled.ts +41 -0
- package/src/components/form/types.ts +21 -0
- package/src/context/AIWidgetProvider.tsx +6 -0
- package/src/context/hooks.ts +93 -0
- package/src/features.ts +18 -0
- package/src/hooks/chat-scroll.ts +14 -0
- package/src/index.ts +8 -0
- package/src/layout.css +119 -0
- package/src/right-panel/DefaultPanel.tsx +67 -0
- package/src/right-panel/RightPanel.tsx +6 -0
- package/src/right-panel/RightPanelProvider.tsx +20 -0
- package/src/right-panel/hooks.tsx +47 -0
- package/src/state/ChatEntry.ts +95 -0
- package/src/state/ChatState.ts +85 -0
- package/src/state/ChatTabsController.ts +55 -0
- package/src/state/ObservableState.ts +35 -0
- package/src/state/WidgetState.ts +42 -0
- package/src/types.ts +20 -0
- package/src/utils/chat.ts +30 -0
- package/src/utils/date.ts +40 -0
- package/src/views/Agents.tsx +1 -0
- package/src/views/Chat/AgentInfo.tsx +17 -0
- package/src/views/Chat/ChatMessage.tsx +89 -0
- package/src/views/Chat/ChatMessages.tsx +16 -0
- package/src/views/Chat/index.tsx +11 -0
- package/src/views/Chat/styled.ts +116 -0
- package/src/views/ChatTabSelection.tsx +65 -0
- package/src/views/Editor.tsx +1 -0
- package/src/views/Home.tsx +109 -0
- package/src/views/KnowledgeSources.tsx +115 -0
- package/src/views/MessageInput/ButtonGroup.tsx +84 -0
- package/src/views/MessageInput/InfoBar.tsx +69 -0
- package/src/views/MessageInput/dictionary.ts +36 -0
- package/src/views/MessageInput/index.tsx +79 -0
- package/src/views/MessageInput/styled.ts +196 -0
- package/src/views/MinimizedHeader.tsx +94 -0
- package/src/views/Stacks.tsx +104 -0
- package/src/views/Workspaces.tsx +88 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ChatTabsController } from './ChatTabsController'
|
|
2
|
+
import { ObservableState } from './ObservableState'
|
|
3
|
+
|
|
4
|
+
export interface WidgetProperties {
|
|
5
|
+
isStackSelectionOpen?: boolean,
|
|
6
|
+
isWorkspaceSelectionOpen?: boolean,
|
|
7
|
+
isAgentSelectionOpen?: boolean,
|
|
8
|
+
isKnowledgeSourceSelectionOpen?: boolean,
|
|
9
|
+
isCodeEditorOpen?: boolean,
|
|
10
|
+
isChatHistoryOpen?: boolean,
|
|
11
|
+
isMinimized?: boolean,
|
|
12
|
+
code?: string,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const panelKeys: (keyof WidgetProperties)[] = [
|
|
16
|
+
'isAgentSelectionOpen', 'isChatHistoryOpen', 'isCodeEditorOpen', 'isKnowledgeSourceSelectionOpen', 'isStackSelectionOpen',
|
|
17
|
+
'isWorkspaceSelectionOpen',
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export class WidgetState extends ObservableState<WidgetProperties> {
|
|
21
|
+
readonly chatTabs: ChatTabsController
|
|
22
|
+
|
|
23
|
+
constructor(initial: WidgetProperties = {}, chatTabs?: ChatTabsController) {
|
|
24
|
+
super(initial)
|
|
25
|
+
this.chatTabs = chatTabs ?? new ChatTabsController()
|
|
26
|
+
this.createCloseOthersBehavior()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private createCloseOthersBehavior() {
|
|
30
|
+
panelKeys.forEach((key) => {
|
|
31
|
+
this.onChange(key, (value) => {
|
|
32
|
+
if (value) this.closeOtherPanels(key)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private closeOtherPanels(leaveOpen: keyof WidgetProperties) {
|
|
38
|
+
panelKeys.forEach((key) => {
|
|
39
|
+
if (key !== leaveOpen) this.set(key, false)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WithStyle } from '@stack-spot/portal-theme'
|
|
2
|
+
|
|
3
|
+
export interface WithChildren<T = React.ReactNode> {
|
|
4
|
+
children?: T,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type PropsOf<T extends React.FunctionComponent> = T extends React.FunctionComponent<infer P> ? P : never
|
|
8
|
+
|
|
9
|
+
export interface ButtonAction extends WithStyle {
|
|
10
|
+
icon?: React.ReactElement,
|
|
11
|
+
color?: string,
|
|
12
|
+
label: string,
|
|
13
|
+
onClick: () => void,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MinimizedActions {
|
|
17
|
+
onCollapse?: () => void,
|
|
18
|
+
onExpand?: () => void,
|
|
19
|
+
onClose?: () => void,
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FixedChatRequest } from '@stack-spot/portal-network'
|
|
2
|
+
import { getLanguage } from '@stack-spot/portal-translate'
|
|
3
|
+
import { ulid } from 'ulid'
|
|
4
|
+
import { ChatState, MessageInterceptor } from '../state/ChatState'
|
|
5
|
+
import { WidgetState } from '../state/WidgetState'
|
|
6
|
+
|
|
7
|
+
let next = 1
|
|
8
|
+
|
|
9
|
+
export function createNewChat(widget: WidgetState, interceptors: MessageInterceptor[]) {
|
|
10
|
+
const id = ulid()
|
|
11
|
+
widget.chatTabs.add(new ChatState({ id, initial: { label: `Chat ${next}` }, interceptors }))
|
|
12
|
+
widget.chatTabs.select(id)
|
|
13
|
+
next++
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildConversationContext(state: ChatState): FixedChatRequest['context'] {
|
|
17
|
+
return {
|
|
18
|
+
workspace: state.get('workspace')?.id,
|
|
19
|
+
conversation_id: state.id,
|
|
20
|
+
stack_id: state.get('stack')?.id,
|
|
21
|
+
language: getLanguage(),
|
|
22
|
+
knowledge_sources: state.get('knowledgeSources')?.map(ks => ks.id),
|
|
23
|
+
agent_id: state.get('agent')?.id,
|
|
24
|
+
agent_built_in: !!state.get('agent'),
|
|
25
|
+
os: navigator.userAgent,
|
|
26
|
+
platform: 'web-widget',
|
|
27
|
+
platform_version: navigator.userAgent,
|
|
28
|
+
stackspot_ai_version: 'alpha',
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Dictionary, Language, getLanguage, useLanguage } from '@stack-spot/portal-translate'
|
|
2
|
+
|
|
3
|
+
const OneDay = 24 * 60 * 60 * 1000
|
|
4
|
+
const timeFormatOptions: Intl.DateTimeFormatOptions = { hour: '2-digit', minute:'2-digit' }
|
|
5
|
+
const fullFormatOptions: Intl.DateTimeFormatOptions = {
|
|
6
|
+
...timeFormatOptions,
|
|
7
|
+
day: '2-digit',
|
|
8
|
+
month: 'long',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export function formatDateForChatMessage(date: Date, language: Language = getLanguage()) {
|
|
13
|
+
const formatted: string[] = []
|
|
14
|
+
const now = new Date()
|
|
15
|
+
const isToday = date.toDateString() === now.toDateString()
|
|
16
|
+
const isYesterday = now.getTime() - date.getTime() < OneDay
|
|
17
|
+
if (isToday) formatted.push(dictionary[language].today)
|
|
18
|
+
else if (isYesterday) formatted.push(dictionary[language].yesterday)
|
|
19
|
+
const options = isToday || isYesterday ? timeFormatOptions : fullFormatOptions
|
|
20
|
+
formatted.push(date.toLocaleString([], options))
|
|
21
|
+
return formatted.join(', ')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useDateFormatter() {
|
|
25
|
+
const language = useLanguage()
|
|
26
|
+
return {
|
|
27
|
+
formatForChatMessage: (date: Date) => formatDateForChatMessage(date, language),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const dictionary = {
|
|
32
|
+
en: {
|
|
33
|
+
today: 'Today',
|
|
34
|
+
yesterday: 'Yesterday',
|
|
35
|
+
},
|
|
36
|
+
pt: {
|
|
37
|
+
today: 'Hoje',
|
|
38
|
+
yesterday: 'Ontem',
|
|
39
|
+
},
|
|
40
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Agents = () => null
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Text } from '@citric/core'
|
|
2
|
+
import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
agentId?: string,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// todo: retrieve agent data and render accordingly
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
+
export const AgentInfo = ({ agentId: _agentId }: Props) => (
|
|
11
|
+
<>
|
|
12
|
+
<div className="agent-image-wrapper">
|
|
13
|
+
<MiniLogo className="agent-image" />
|
|
14
|
+
</div>
|
|
15
|
+
<Text appearance="body2">Stackspot AI</Text>
|
|
16
|
+
</>
|
|
17
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { IconBox, Text } from '@citric/core'
|
|
2
|
+
import { Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
|
|
3
|
+
import { Avatar, IconButton } from '@citric/ui'
|
|
4
|
+
import { aiClient } from '@stack-spot/portal-network'
|
|
5
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
6
|
+
import { useMemo, useRef, useState } from 'react'
|
|
7
|
+
import { Markdown } from '../../components/Markdown'
|
|
8
|
+
import { useChatEntry } from '../../context/hooks'
|
|
9
|
+
import { useChatScrollToBottomEffect } from '../../hooks/chat-scroll'
|
|
10
|
+
import { ChatEntry } from '../../state/ChatEntry'
|
|
11
|
+
import { useDateFormatter } from '../../utils/date'
|
|
12
|
+
import { AgentInfo } from './AgentInfo'
|
|
13
|
+
|
|
14
|
+
export const ChatMessage = ({ message, username }: { message: ChatEntry, username: string }) => {
|
|
15
|
+
const t = useTranslate(dictionary)
|
|
16
|
+
const [liked, setLiked] = useState<boolean | undefined>()
|
|
17
|
+
const entry = useChatEntry(message)
|
|
18
|
+
const dateFormatter = useDateFormatter()
|
|
19
|
+
const userInfo = entry.agent === 'user' ? <Avatar size="xs">{username}</Avatar> : <AgentInfo agentId={entry.agentId} />
|
|
20
|
+
const date = new Date(entry.updated ?? '')
|
|
21
|
+
const shouldShowDate = entry.updated && !isNaN(date.getTime())
|
|
22
|
+
const ref = useRef<HTMLLIElement>(null)
|
|
23
|
+
useChatScrollToBottomEffect(ref, [entry])
|
|
24
|
+
|
|
25
|
+
const { like, dislike } = useMemo(() => {
|
|
26
|
+
async function feedback(like: boolean) {
|
|
27
|
+
if (!entry.messageId || like === liked) return
|
|
28
|
+
setLiked(like)
|
|
29
|
+
try {
|
|
30
|
+
await aiClient.createEvent.mutate({
|
|
31
|
+
body: [{
|
|
32
|
+
feedback: like ? 'LIKE' : 'DISLIKE',
|
|
33
|
+
message_id: entry.messageId,
|
|
34
|
+
type: 'user_feedback_provided',
|
|
35
|
+
code: '',
|
|
36
|
+
generated_at: Math.floor(new Date().getTime() / 1000),
|
|
37
|
+
size: 0,
|
|
38
|
+
}],
|
|
39
|
+
})
|
|
40
|
+
} catch {
|
|
41
|
+
setLiked(liked)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
like: () => feedback(true),
|
|
47
|
+
dislike: () => feedback(false),
|
|
48
|
+
}
|
|
49
|
+
}, [entry.messageId, liked])
|
|
50
|
+
|
|
51
|
+
return (entry.content || entry.error) && (
|
|
52
|
+
<li className={entry.agent} ref={ref}>
|
|
53
|
+
<div className="chat-message">
|
|
54
|
+
<div className="user-info">{userInfo}</div>
|
|
55
|
+
{entry.content && <div className="message-content">
|
|
56
|
+
{entry.type === 'md' ? <Markdown>{entry.content}</Markdown> : <p className="plain-text">{entry.content}</p>}
|
|
57
|
+
</div>}
|
|
58
|
+
</div>
|
|
59
|
+
{entry.error && (
|
|
60
|
+
<div className="error">
|
|
61
|
+
<IconBox size="xs"><TimesCircle /></IconBox>
|
|
62
|
+
<Text appearance="microtext1">{entry.error}</Text>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
<div className="message-footer">
|
|
66
|
+
{entry.agent === 'bot' && entry.messageId && !entry.error && <div className="message-actions">
|
|
67
|
+
<IconButton title={t.like} aria-label={t.like} onClick={like}>
|
|
68
|
+
{liked === true ? <LikeFill /> : <Like />}
|
|
69
|
+
</IconButton>
|
|
70
|
+
<IconButton title={t.dislike} aria-label={t.dislike} onClick={dislike}>
|
|
71
|
+
{liked === false ? <DislikeFill /> : <Dislike />}
|
|
72
|
+
</IconButton>
|
|
73
|
+
</div>}
|
|
74
|
+
{shouldShowDate && <Text appearance="microtext1" className="chat-date">{dateFormatter.formatForChatMessage(date)}</Text>}
|
|
75
|
+
</div>
|
|
76
|
+
</li>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const dictionary = {
|
|
81
|
+
en: {
|
|
82
|
+
like: 'Like',
|
|
83
|
+
dislike: 'Dislike',
|
|
84
|
+
},
|
|
85
|
+
pt: {
|
|
86
|
+
like: 'Gostei',
|
|
87
|
+
dislike: 'Não gostei',
|
|
88
|
+
},
|
|
89
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react'
|
|
2
|
+
import { useChatMessages } from '../../context/hooks'
|
|
3
|
+
import { ChatMessage } from './ChatMessage'
|
|
4
|
+
import { ChatList } from './styled'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
username: string,
|
|
8
|
+
chatId: string,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ChatMessages = ({ chatId, username }: Props) => {
|
|
12
|
+
const messages = useChatMessages(chatId)
|
|
13
|
+
const items = useMemo(() => messages.map(m => <ChatMessage key={m.id} message={m} username={username} />), [messages])
|
|
14
|
+
const ref = useRef<HTMLUListElement>(null)
|
|
15
|
+
return <ChatList ref={ref}>{items}</ChatList>
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useChatTabs } from '../../context/hooks'
|
|
2
|
+
import { ChatMessages } from './ChatMessages'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
username: string,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Chat = ({ username }: Props) => {
|
|
9
|
+
const { active } = useChatTabs()
|
|
10
|
+
return <ChatMessages key={active} chatId={active} username={username} />
|
|
11
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
2
|
+
import { styled } from 'styled-components'
|
|
3
|
+
|
|
4
|
+
export const ChatList = styled.ul`
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
justify-content: end;
|
|
8
|
+
gap: 20px;
|
|
9
|
+
margin: 0 5px;
|
|
10
|
+
padding: 0;
|
|
11
|
+
flex: 1;
|
|
12
|
+
|
|
13
|
+
.error {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: row;
|
|
16
|
+
gap: 8px;
|
|
17
|
+
padding: 8px 10px;
|
|
18
|
+
background-color: ${theme.color.danger[500]};
|
|
19
|
+
color: ${theme.color.danger.contrastText};
|
|
20
|
+
border-radius: 8px;
|
|
21
|
+
align-self: start;
|
|
22
|
+
|
|
23
|
+
svg {
|
|
24
|
+
fill: ${theme.color.danger.contrastText};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
small {
|
|
28
|
+
margin-top: 1px;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.user-info {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: row;
|
|
35
|
+
gap: 10px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.chat-message {
|
|
39
|
+
p, li {
|
|
40
|
+
line-height: 24px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
a[href] {
|
|
44
|
+
color: inherit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.highlighter {
|
|
48
|
+
background-color: ${theme.color.gray[800]} !important;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.message-footer {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: row;
|
|
55
|
+
|
|
56
|
+
.message-actions {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: row;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.chat-date {
|
|
63
|
+
opacity: 0.6;
|
|
64
|
+
margin-left: auto;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
> li {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: 10px;
|
|
72
|
+
|
|
73
|
+
&.bot, &.system {
|
|
74
|
+
.chat-message {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
gap: 4px;
|
|
78
|
+
|
|
79
|
+
.agent-image-wrapper {
|
|
80
|
+
width: 24px;
|
|
81
|
+
height: 24px;
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
background-color: ${theme.color.light[300]};
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
|
|
88
|
+
.agent-image {
|
|
89
|
+
width: 18px;
|
|
90
|
+
height: 18px;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.user {
|
|
97
|
+
align-items: end;
|
|
98
|
+
|
|
99
|
+
.chat-message {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: row;
|
|
102
|
+
gap: 8px;
|
|
103
|
+
|
|
104
|
+
.message-content {
|
|
105
|
+
padding: 10px;
|
|
106
|
+
background-color: ${theme.color.light[500]};
|
|
107
|
+
border-radius: 4px;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.plain-text {
|
|
114
|
+
margin: 0
|
|
115
|
+
}
|
|
116
|
+
`
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Clock, Plus } from '@citric/icons'
|
|
2
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import { TabManager } from '../components/TabManager'
|
|
5
|
+
import { useChatState, useChatTabs, useWidget } from '../context/hooks'
|
|
6
|
+
import { MessageInterceptor } from '../state/ChatState'
|
|
7
|
+
import { ButtonAction } from '../types'
|
|
8
|
+
import { createNewChat } from '../utils/chat'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
history?: boolean,
|
|
12
|
+
interceptors: MessageInterceptor[],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const TabLabel = ({ id }: { id: string }) => {
|
|
16
|
+
const label = useChatState(id, 'label')
|
|
17
|
+
return <div title={label}>{label}</div>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ChatTabSelection = ({ history, interceptors }: Props) => {
|
|
21
|
+
const t = useTranslate(dictionary)
|
|
22
|
+
const widget = useWidget()
|
|
23
|
+
const { active, chats } = useChatTabs()
|
|
24
|
+
|
|
25
|
+
const buttons = useMemo<ButtonAction[]>(
|
|
26
|
+
() => {
|
|
27
|
+
const actions: ButtonAction[] = [{
|
|
28
|
+
icon: <Plus />,
|
|
29
|
+
label: t.newChat,
|
|
30
|
+
onClick: () => createNewChat(widget, interceptors),
|
|
31
|
+
}]
|
|
32
|
+
if (history) {
|
|
33
|
+
actions.push({
|
|
34
|
+
icon: <Clock />,
|
|
35
|
+
label: t.openHistory,
|
|
36
|
+
className: 'test',
|
|
37
|
+
style: { marginLeft: 'auto' },
|
|
38
|
+
onClick: () => { /* todo */ },
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
return actions
|
|
42
|
+
},
|
|
43
|
+
[history],
|
|
44
|
+
)
|
|
45
|
+
return <TabManager
|
|
46
|
+
tabs={chats}
|
|
47
|
+
active={active}
|
|
48
|
+
renderLabel={({ id }) => <TabLabel id={id} />}
|
|
49
|
+
keygen={({ id }) => id}
|
|
50
|
+
onRemove={({ id }) => widget.chatTabs.remove(id)}
|
|
51
|
+
onSelect={({ id }) => widget.chatTabs.select(id)}
|
|
52
|
+
buttons={buttons}
|
|
53
|
+
/>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const dictionary = {
|
|
57
|
+
en: {
|
|
58
|
+
openHistory: 'Open chat history',
|
|
59
|
+
newChat: 'New chat',
|
|
60
|
+
},
|
|
61
|
+
pt: {
|
|
62
|
+
openHistory: 'Abrir histórico da conversa',
|
|
63
|
+
newChat: 'Novo chat',
|
|
64
|
+
},
|
|
65
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Editor = () => null
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { FaceSmile, KnowledgeSource, QuickCommand } from '@citric/icons'
|
|
2
|
+
import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
3
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
4
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
5
|
+
import { styled } from 'styled-components'
|
|
6
|
+
import { QuickStartButton } from '../components/QuickStartButton'
|
|
7
|
+
import { useCurrentChat } from '../context/hooks'
|
|
8
|
+
import { ChatEntry } from '../state/ChatEntry'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
username: string,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const HomeBox = styled.div`
|
|
15
|
+
margin: auto;
|
|
16
|
+
|
|
17
|
+
.title, .subtitle {
|
|
18
|
+
font-family: 'San Francisco';
|
|
19
|
+
font-size: 26px;
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
margin: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.title {
|
|
25
|
+
display: inline-block;
|
|
26
|
+
background: linear-gradient(72.81deg, #FF9900 0.96%, #FF6633 100%);
|
|
27
|
+
background-clip: text;
|
|
28
|
+
margin-bottom: 10px;
|
|
29
|
+
color: transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.subtitle {
|
|
33
|
+
color: #A0A0A0;
|
|
34
|
+
margin-bottom: 20px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.shortcuts {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: row;
|
|
40
|
+
gap: 15px;
|
|
41
|
+
li {
|
|
42
|
+
flex: 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
export const Home = ({ username }: Props) => {
|
|
48
|
+
const t = useTranslate(dictionary)
|
|
49
|
+
const chat = useCurrentChat()
|
|
50
|
+
|
|
51
|
+
function send(message: string) {
|
|
52
|
+
chat.pushMessage(ChatEntry.createUserEntry(message))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<HomeBox className="home-page">
|
|
57
|
+
<h2 className="title">{t.hello}, {username}</h2>
|
|
58
|
+
<h3 className="subtitle">{t.subtitle}</h3>
|
|
59
|
+
<div className="shortcuts">
|
|
60
|
+
<QuickStartButton
|
|
61
|
+
label={t['question.meta']}
|
|
62
|
+
onClick={() => send(t['question.meta'])}
|
|
63
|
+
icon={<MiniLogo />}
|
|
64
|
+
background={theme.color.orange[50]}
|
|
65
|
+
/>
|
|
66
|
+
<QuickStartButton
|
|
67
|
+
label={t['question.ks']}
|
|
68
|
+
onClick={() => send(t['question.ks'])}
|
|
69
|
+
icon={<KnowledgeSource />}
|
|
70
|
+
background={theme.color.cyan[50]}
|
|
71
|
+
color={theme.color.cyan[600]}
|
|
72
|
+
/>
|
|
73
|
+
<QuickStartButton
|
|
74
|
+
label={t['question.agents']}
|
|
75
|
+
onClick={() => send(t['question.agents'])}
|
|
76
|
+
icon={<FaceSmile />}
|
|
77
|
+
background={theme.color.pink[50]}
|
|
78
|
+
color={theme.color.pink[600]}
|
|
79
|
+
/>
|
|
80
|
+
<QuickStartButton
|
|
81
|
+
label={t['question.qc']}
|
|
82
|
+
onClick={() => send(t['question.qc'])}
|
|
83
|
+
icon={<QuickCommand />}
|
|
84
|
+
background={theme.color.purple[50]}
|
|
85
|
+
color={theme.color.purple[600]}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</HomeBox>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const dictionary = {
|
|
93
|
+
en: {
|
|
94
|
+
hello: 'Hello',
|
|
95
|
+
subtitle: "Let's innovate and streamline your coding journey together. Want to lead the change? Just ask!",
|
|
96
|
+
'question.meta': 'What is StackSpot AI?',
|
|
97
|
+
'question.ks': 'What are Knowledge Sources?',
|
|
98
|
+
'question.agents': 'How do Agents work?',
|
|
99
|
+
'question.qc': 'What is a Quick Command?',
|
|
100
|
+
},
|
|
101
|
+
pt: {
|
|
102
|
+
hello: 'Olá',
|
|
103
|
+
subtitle: 'Vamos inovar e simplificar sua jornada de programação juntos. Quer liderar a mudança? É só perguntar!',
|
|
104
|
+
'question.meta': 'O que é a StackSpot AI?',
|
|
105
|
+
'question.ks': 'O que são Knowledge Sources?',
|
|
106
|
+
'question.agents': 'Como funcionam os agentes?',
|
|
107
|
+
'question.qc': 'O que é um Quick Command?',
|
|
108
|
+
},
|
|
109
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Button } from '@citric/core'
|
|
2
|
+
import { Search } from '@citric/icons'
|
|
3
|
+
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
4
|
+
import { aiClient } from '@stack-spot/portal-network'
|
|
5
|
+
import { KnowledgeSourceItemResponse, VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
|
|
6
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
8
|
+
import { DescribedCheckboxGroup } from '../components/form/DescribedCheckboxGroup'
|
|
9
|
+
import { IconInput } from '../components/IconInput'
|
|
10
|
+
import { RightPanelTabs } from '../components/RightPanelTabs'
|
|
11
|
+
import { useCurrentChat, useWidget, useWidgetState } from '../context/hooks'
|
|
12
|
+
import { useRightPanel } from '../right-panel/hooks'
|
|
13
|
+
|
|
14
|
+
export const KnowledgeSources = () => {
|
|
15
|
+
const t = useTranslate(dictionary)
|
|
16
|
+
const isKnowledgeSourceSelectionOpen = useWidgetState('isKnowledgeSourceSelectionOpen')
|
|
17
|
+
const { open } = useRightPanel()
|
|
18
|
+
const widget = useWidget()
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (isKnowledgeSourceSelectionOpen) open(
|
|
22
|
+
<KnowledgeSourcesPanel />,
|
|
23
|
+
{ title: t.title, description: t.description, onClose: () => widget.set('isKnowledgeSourceSelectionOpen', false) },
|
|
24
|
+
)
|
|
25
|
+
}, [isKnowledgeSourceSelectionOpen, t])
|
|
26
|
+
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const KnowledgeSourcesPanel = () => {
|
|
31
|
+
const t = useTranslate(dictionary)
|
|
32
|
+
|
|
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" /> },
|
|
37
|
+
]} />
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const KnowledgeSourcesTab = ({ visibility }: { visibility: VisibilityLevelEnum }) => {
|
|
41
|
+
const t = useTranslate(dictionary)
|
|
42
|
+
const { close } = useRightPanel()
|
|
43
|
+
const chat = useCurrentChat()
|
|
44
|
+
const [filter, setFilter] = useState('')
|
|
45
|
+
const knowledgeSources = aiClient.knowledgeSources.useQuery({ visibility, order: 'a-to-z' })
|
|
46
|
+
const [hasChanged, setChanged] = useState(false)
|
|
47
|
+
const [value, setValue] = useState<KnowledgeSourceItemResponse[]>((() => {
|
|
48
|
+
const currentlySelected = chat.get('knowledgeSources')?.map(ks => ks.id)
|
|
49
|
+
return knowledgeSources.filter(ks => currentlySelected?.includes(ks.id))
|
|
50
|
+
})())
|
|
51
|
+
const filtered = useMemo(
|
|
52
|
+
() => filter
|
|
53
|
+
? knowledgeSources.filter(ks => value.includes(ks) || ks.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
54
|
+
: knowledgeSources,
|
|
55
|
+
[knowledgeSources, filter, value],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
function submit() {
|
|
59
|
+
if (value) chat.set('knowledgeSources', value.map(({ id, name }) => ({ id, label: name })))
|
|
60
|
+
close()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<div className="content">
|
|
66
|
+
<IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
|
|
67
|
+
{!!filtered.length && <DescribedCheckboxGroup
|
|
68
|
+
options={filtered}
|
|
69
|
+
keygen={ks => ks.id}
|
|
70
|
+
value={value}
|
|
71
|
+
onChange={(value) => {
|
|
72
|
+
setValue(value)
|
|
73
|
+
setChanged(true)
|
|
74
|
+
}}
|
|
75
|
+
renderLabel={ks => ks.name}
|
|
76
|
+
renderDescription={ks => ks.description}
|
|
77
|
+
optionClassName={ks => (filter && !ks.name.includes(filter) && value.includes(ks)) ? 'filtered-out' : ''}
|
|
78
|
+
className="option-list"
|
|
79
|
+
/>}
|
|
80
|
+
{!!knowledgeSources.length && !filtered.length && (
|
|
81
|
+
<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />
|
|
82
|
+
)}
|
|
83
|
+
{!knowledgeSources.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
|
|
84
|
+
</div>
|
|
85
|
+
<Button onClick={submit} disabled={!hasChanged}>{t.apply}</Button>
|
|
86
|
+
</>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const dictionary = {
|
|
91
|
+
en: {
|
|
92
|
+
title: 'Knowledge Sources',
|
|
93
|
+
description: 'By selecting one or more knowledge sources, they will be consulted to generate the responses.',
|
|
94
|
+
personal: 'Personal',
|
|
95
|
+
shared: 'Shared',
|
|
96
|
+
account: 'Account',
|
|
97
|
+
apply: 'Apply',
|
|
98
|
+
noSearchResults: "Your search didn't yield results.",
|
|
99
|
+
noSearchResultsDescription: 'Please, try another search term.',
|
|
100
|
+
noData: 'There are no knowledge sources in this category yet.',
|
|
101
|
+
noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new knowledge sources.',
|
|
102
|
+
},
|
|
103
|
+
pt: {
|
|
104
|
+
title: 'Knowledge Sources',
|
|
105
|
+
description: 'Ao selecionar um ou mais knowledge sources, eles serão consultados para gerar as respostas.',
|
|
106
|
+
personal: 'Pessoal',
|
|
107
|
+
shared: 'Compartilhado',
|
|
108
|
+
account: 'Conta',
|
|
109
|
+
apply: 'Aplicar',
|
|
110
|
+
noSearchResults: 'Sua busca não produziu resultados.',
|
|
111
|
+
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
112
|
+
noData: 'Ainda não há knowledge sources nesta categoria.',
|
|
113
|
+
noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos knowledge sources.',
|
|
114
|
+
},
|
|
115
|
+
} satisfies Dictionary
|