@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.
Files changed (329) hide show
  1. package/dist/StackspotAIWidget.d.ts +13 -0
  2. package/dist/StackspotAIWidget.d.ts.map +1 -0
  3. package/dist/StackspotAIWidget.js +32 -0
  4. package/dist/StackspotAIWidget.js.map +1 -0
  5. package/dist/chat-interceptors/quick-commands.d.ts +4 -0
  6. package/dist/chat-interceptors/quick-commands.d.ts.map +1 -0
  7. package/dist/chat-interceptors/quick-commands.js +11 -0
  8. package/dist/chat-interceptors/quick-commands.js.map +1 -0
  9. package/dist/chat-interceptors/send-message.d.ts +4 -0
  10. package/dist/chat-interceptors/send-message.d.ts.map +1 -0
  11. package/dist/chat-interceptors/send-message.js +36 -0
  12. package/dist/chat-interceptors/send-message.js.map +1 -0
  13. package/dist/components/Accordion.d.ts +10 -0
  14. package/dist/components/Accordion.d.ts.map +1 -0
  15. package/dist/components/Accordion.js +46 -0
  16. package/dist/components/Accordion.js.map +1 -0
  17. package/dist/components/AdaptiveTextArea.d.ts +10 -0
  18. package/dist/components/AdaptiveTextArea.d.ts.map +1 -0
  19. package/dist/components/AdaptiveTextArea.js +28 -0
  20. package/dist/components/AdaptiveTextArea.js.map +1 -0
  21. package/dist/components/Code.d.ts +19 -0
  22. package/dist/components/Code.d.ts.map +1 -0
  23. package/dist/components/Code.js +112 -0
  24. package/dist/components/Code.js.map +1 -0
  25. package/dist/components/Editor.d.ts +9 -0
  26. package/dist/components/Editor.d.ts.map +1 -0
  27. package/dist/components/Editor.js +2 -0
  28. package/dist/components/Editor.js.map +1 -0
  29. package/dist/components/FadingOverflow.d.ts +12 -0
  30. package/dist/components/FadingOverflow.d.ts.map +1 -0
  31. package/dist/components/FadingOverflow.js +216 -0
  32. package/dist/components/FadingOverflow.js.map +1 -0
  33. package/dist/components/FallbackBoundary/ErrorBoundary.d.ts +30 -0
  34. package/dist/components/FallbackBoundary/ErrorBoundary.d.ts.map +1 -0
  35. package/dist/components/FallbackBoundary/ErrorBoundary.js +38 -0
  36. package/dist/components/FallbackBoundary/ErrorBoundary.js.map +1 -0
  37. package/dist/components/FallbackBoundary/Loading.d.ts +2 -0
  38. package/dist/components/FallbackBoundary/Loading.d.ts.map +1 -0
  39. package/dist/components/FallbackBoundary/Loading.js +12 -0
  40. package/dist/components/FallbackBoundary/Loading.js.map +1 -0
  41. package/dist/components/FallbackBoundary/index.d.ts +6 -0
  42. package/dist/components/FallbackBoundary/index.d.ts.map +1 -0
  43. package/dist/components/FallbackBoundary/index.js +9 -0
  44. package/dist/components/FallbackBoundary/index.js.map +1 -0
  45. package/dist/components/HistoryList.d.ts +13 -0
  46. package/dist/components/HistoryList.d.ts.map +1 -0
  47. package/dist/components/HistoryList.js +4 -0
  48. package/dist/components/HistoryList.js.map +1 -0
  49. package/dist/components/IconInput.d.ts +7 -0
  50. package/dist/components/IconInput.d.ts.map +1 -0
  51. package/dist/components/IconInput.js +58 -0
  52. package/dist/components/IconInput.js.map +1 -0
  53. package/dist/components/Markdown.d.ts +9 -0
  54. package/dist/components/Markdown.d.ts.map +1 -0
  55. package/dist/components/Markdown.js +17 -0
  56. package/dist/components/Markdown.js.map +1 -0
  57. package/dist/components/OverlayMenu.d.ts +9 -0
  58. package/dist/components/OverlayMenu.d.ts.map +1 -0
  59. package/dist/components/OverlayMenu.js +2 -0
  60. package/dist/components/OverlayMenu.js.map +1 -0
  61. package/dist/components/ProgressBar.d.ts +11 -0
  62. package/dist/components/ProgressBar.d.ts.map +1 -0
  63. package/dist/components/ProgressBar.js +126 -0
  64. package/dist/components/ProgressBar.js.map +1 -0
  65. package/dist/components/QuickStartButton.d.ts +8 -0
  66. package/dist/components/QuickStartButton.d.ts.map +1 -0
  67. package/dist/components/QuickStartButton.js +40 -0
  68. package/dist/components/QuickStartButton.js.map +1 -0
  69. package/dist/components/RightPanelForm.d.ts +5 -0
  70. package/dist/components/RightPanelForm.d.ts.map +1 -0
  71. package/dist/components/RightPanelForm.js +45 -0
  72. package/dist/components/RightPanelForm.js.map +1 -0
  73. package/dist/components/RightPanelTabs.d.ts +10 -0
  74. package/dist/components/RightPanelTabs.d.ts.map +1 -0
  75. package/dist/components/RightPanelTabs.js +20 -0
  76. package/dist/components/RightPanelTabs.js.map +1 -0
  77. package/dist/components/TabManager.d.ts +34 -0
  78. package/dist/components/TabManager.d.ts.map +1 -0
  79. package/dist/components/TabManager.js +158 -0
  80. package/dist/components/TabManager.js.map +1 -0
  81. package/dist/components/Tooltip/Tooltip.d.ts +11 -0
  82. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -0
  83. package/dist/components/Tooltip/Tooltip.js +9 -0
  84. package/dist/components/Tooltip/Tooltip.js.map +1 -0
  85. package/dist/components/Tooltip/TooltipAPI.d.ts +10 -0
  86. package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -0
  87. package/dist/components/Tooltip/TooltipAPI.js +48 -0
  88. package/dist/components/Tooltip/TooltipAPI.js.map +1 -0
  89. package/dist/components/Tooltip/context.d.ts +5 -0
  90. package/dist/components/Tooltip/context.d.ts.map +1 -0
  91. package/dist/components/Tooltip/context.js +18 -0
  92. package/dist/components/Tooltip/context.js.map +1 -0
  93. package/dist/components/Tooltip/index.d.ts +3 -0
  94. package/dist/components/Tooltip/index.d.ts.map +1 -0
  95. package/dist/components/Tooltip/index.js +3 -0
  96. package/dist/components/Tooltip/index.js.map +1 -0
  97. package/dist/components/Tooltip/style.d.ts +4 -0
  98. package/dist/components/Tooltip/style.d.ts.map +1 -0
  99. package/dist/components/Tooltip/style.js +23 -0
  100. package/dist/components/Tooltip/style.js.map +1 -0
  101. package/dist/components/Tooltip/types.d.ts +8 -0
  102. package/dist/components/Tooltip/types.d.ts.map +1 -0
  103. package/dist/components/Tooltip/types.js +2 -0
  104. package/dist/components/Tooltip/types.js.map +1 -0
  105. package/dist/components/form/DescribedCheckboxGroup.d.ts +3 -0
  106. package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -0
  107. package/dist/components/form/DescribedCheckboxGroup.js +23 -0
  108. package/dist/components/form/DescribedCheckboxGroup.js.map +1 -0
  109. package/dist/components/form/DescribedRadioGroup.d.ts +3 -0
  110. package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -0
  111. package/dist/components/form/DescribedRadioGroup.js +18 -0
  112. package/dist/components/form/DescribedRadioGroup.js.map +1 -0
  113. package/dist/components/form/styled.d.ts +2 -0
  114. package/dist/components/form/styled.d.ts.map +1 -0
  115. package/dist/components/form/styled.js +41 -0
  116. package/dist/components/form/styled.js.map +1 -0
  117. package/dist/components/form/types.d.ts +19 -0
  118. package/dist/components/form/types.d.ts.map +1 -0
  119. package/dist/components/form/types.js +2 -0
  120. package/dist/components/form/types.js.map +1 -0
  121. package/dist/context/AIWidgetProvider.d.ts +4 -0
  122. package/dist/context/AIWidgetProvider.d.ts.map +1 -0
  123. package/dist/context/AIWidgetProvider.js +4 -0
  124. package/dist/context/AIWidgetProvider.js.map +1 -0
  125. package/dist/context/hooks.d.ts +18 -0
  126. package/dist/context/hooks.d.ts.map +1 -0
  127. package/dist/context/hooks.js +82 -0
  128. package/dist/context/hooks.js.map +1 -0
  129. package/dist/features.d.ts +13 -0
  130. package/dist/features.d.ts.map +1 -0
  131. package/dist/features.js +6 -0
  132. package/dist/features.js.map +1 -0
  133. package/dist/hooks/chat-scroll.d.ts +7 -0
  134. package/dist/hooks/chat-scroll.d.ts.map +1 -0
  135. package/dist/hooks/chat-scroll.js +15 -0
  136. package/dist/hooks/chat-scroll.js.map +1 -0
  137. package/dist/index.d.ts +9 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +9 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/layout.css +119 -0
  142. package/dist/right-panel/DefaultPanel.d.ts +9 -0
  143. package/dist/right-panel/DefaultPanel.d.ts.map +1 -0
  144. package/dist/right-panel/DefaultPanel.js +46 -0
  145. package/dist/right-panel/DefaultPanel.js.map +1 -0
  146. package/dist/right-panel/RightPanel.d.ts +2 -0
  147. package/dist/right-panel/RightPanel.d.ts.map +1 -0
  148. package/dist/right-panel/RightPanel.js +6 -0
  149. package/dist/right-panel/RightPanel.js.map +1 -0
  150. package/dist/right-panel/RightPanelProvider.d.ts +13 -0
  151. package/dist/right-panel/RightPanelProvider.d.ts.map +1 -0
  152. package/dist/right-panel/RightPanelProvider.js +10 -0
  153. package/dist/right-panel/RightPanelProvider.js.map +1 -0
  154. package/dist/right-panel/hooks.d.ts +13 -0
  155. package/dist/right-panel/hooks.d.ts.map +1 -0
  156. package/dist/right-panel/hooks.js +39 -0
  157. package/dist/right-panel/hooks.js.map +1 -0
  158. package/dist/state/ChatEntry.d.ts +48 -0
  159. package/dist/state/ChatEntry.d.ts.map +1 -0
  160. package/dist/state/ChatEntry.js +55 -0
  161. package/dist/state/ChatEntry.js.map +1 -0
  162. package/dist/state/ChatState.d.ts +46 -0
  163. package/dist/state/ChatState.d.ts.map +1 -0
  164. package/dist/state/ChatState.js +53 -0
  165. package/dist/state/ChatState.js.map +1 -0
  166. package/dist/state/ChatTabsController.d.ts +17 -0
  167. package/dist/state/ChatTabsController.d.ts.map +1 -0
  168. package/dist/state/ChatTabsController.js +47 -0
  169. package/dist/state/ChatTabsController.js.map +1 -0
  170. package/dist/state/ObservableState.d.ts +10 -0
  171. package/dist/state/ObservableState.d.ts.map +1 -0
  172. package/dist/state/ObservableState.js +25 -0
  173. package/dist/state/ObservableState.js.map +1 -0
  174. package/dist/state/WidgetState.d.ts +19 -0
  175. package/dist/state/WidgetState.d.ts.map +1 -0
  176. package/dist/state/WidgetState.js +29 -0
  177. package/dist/state/WidgetState.js.map +1 -0
  178. package/dist/types.d.ts +17 -0
  179. package/dist/types.d.ts.map +1 -0
  180. package/dist/types.js +2 -0
  181. package/dist/types.js.map +1 -0
  182. package/dist/utils/chat.d.ts +6 -0
  183. package/dist/utils/chat.d.ts.map +1 -0
  184. package/dist/utils/chat.js +26 -0
  185. package/dist/utils/chat.js.map +1 -0
  186. package/dist/utils/date.d.ts +6 -0
  187. package/dist/utils/date.d.ts.map +1 -0
  188. package/dist/utils/date.js +38 -0
  189. package/dist/utils/date.js.map +1 -0
  190. package/dist/views/Agents.d.ts +2 -0
  191. package/dist/views/Agents.d.ts.map +1 -0
  192. package/dist/views/Agents.js +2 -0
  193. package/dist/views/Agents.js.map +1 -0
  194. package/dist/views/Chat/AgentInfo.d.ts +6 -0
  195. package/dist/views/Chat/AgentInfo.d.ts.map +1 -0
  196. package/dist/views/Chat/AgentInfo.js +7 -0
  197. package/dist/views/Chat/AgentInfo.js.map +1 -0
  198. package/dist/views/Chat/ChatMessage.d.ts +6 -0
  199. package/dist/views/Chat/ChatMessage.d.ts.map +1 -0
  200. package/dist/views/Chat/ChatMessage.js +61 -0
  201. package/dist/views/Chat/ChatMessage.js.map +1 -0
  202. package/dist/views/Chat/ChatMessages.d.ts +7 -0
  203. package/dist/views/Chat/ChatMessages.d.ts.map +1 -0
  204. package/dist/views/Chat/ChatMessages.js +12 -0
  205. package/dist/views/Chat/ChatMessages.js.map +1 -0
  206. package/dist/views/Chat/index.d.ts +6 -0
  207. package/dist/views/Chat/index.d.ts.map +1 -0
  208. package/dist/views/Chat/index.js +8 -0
  209. package/dist/views/Chat/index.js.map +1 -0
  210. package/dist/views/Chat/styled.d.ts +2 -0
  211. package/dist/views/Chat/styled.d.ts.map +1 -0
  212. package/dist/views/Chat/styled.js +116 -0
  213. package/dist/views/Chat/styled.js.map +1 -0
  214. package/dist/views/ChatTabSelection.d.ts +8 -0
  215. package/dist/views/ChatTabSelection.d.ts.map +1 -0
  216. package/dist/views/ChatTabSelection.js +45 -0
  217. package/dist/views/ChatTabSelection.js.map +1 -0
  218. package/dist/views/Editor.d.ts +2 -0
  219. package/dist/views/Editor.d.ts.map +1 -0
  220. package/dist/views/Editor.js +2 -0
  221. package/dist/views/Editor.js.map +1 -0
  222. package/dist/views/Home.d.ts +6 -0
  223. package/dist/views/Home.d.ts.map +1 -0
  224. package/dist/views/Home.js +68 -0
  225. package/dist/views/Home.js.map +1 -0
  226. package/dist/views/KnowledgeSources.d.ts +2 -0
  227. package/dist/views/KnowledgeSources.d.ts.map +1 -0
  228. package/dist/views/KnowledgeSources.js +82 -0
  229. package/dist/views/KnowledgeSources.js.map +1 -0
  230. package/dist/views/MessageInput/ButtonGroup.d.ts +12 -0
  231. package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -0
  232. package/dist/views/MessageInput/ButtonGroup.js +22 -0
  233. package/dist/views/MessageInput/ButtonGroup.js.map +1 -0
  234. package/dist/views/MessageInput/InfoBar.d.ts +2 -0
  235. package/dist/views/MessageInput/InfoBar.d.ts.map +1 -0
  236. package/dist/views/MessageInput/InfoBar.js +28 -0
  237. package/dist/views/MessageInput/InfoBar.js.map +1 -0
  238. package/dist/views/MessageInput/dictionary.d.ts +2 -0
  239. package/dist/views/MessageInput/dictionary.d.ts.map +1 -0
  240. package/dist/views/MessageInput/dictionary.js +35 -0
  241. package/dist/views/MessageInput/dictionary.js.map +1 -0
  242. package/dist/views/MessageInput/index.d.ts +7 -0
  243. package/dist/views/MessageInput/index.d.ts.map +1 -0
  244. package/dist/views/MessageInput/index.js +43 -0
  245. package/dist/views/MessageInput/index.js.map +1 -0
  246. package/dist/views/MessageInput/styled.d.ts +2 -0
  247. package/dist/views/MessageInput/styled.d.ts.map +1 -0
  248. package/dist/views/MessageInput/styled.js +195 -0
  249. package/dist/views/MessageInput/styled.js.map +1 -0
  250. package/dist/views/MinimizedHeader.d.ts +3 -0
  251. package/dist/views/MinimizedHeader.d.ts.map +1 -0
  252. package/dist/views/MinimizedHeader.js +76 -0
  253. package/dist/views/MinimizedHeader.js.map +1 -0
  254. package/dist/views/Stacks.d.ts +2 -0
  255. package/dist/views/Stacks.d.ts.map +1 -0
  256. package/dist/views/Stacks.js +73 -0
  257. package/dist/views/Stacks.js.map +1 -0
  258. package/dist/views/Workspaces.d.ts +2 -0
  259. package/dist/views/Workspaces.d.ts.map +1 -0
  260. package/dist/views/Workspaces.js +59 -0
  261. package/dist/views/Workspaces.js.map +1 -0
  262. package/package.json +52 -0
  263. package/src/StackspotAIWidget.tsx +65 -0
  264. package/src/chat-interceptors/quick-commands.ts +12 -0
  265. package/src/chat-interceptors/send-message.ts +35 -0
  266. package/src/components/Accordion.tsx +64 -0
  267. package/src/components/AdaptiveTextArea.tsx +34 -0
  268. package/src/components/Code.tsx +201 -0
  269. package/src/components/Editor.tsx +12 -0
  270. package/src/components/FadingOverflow.tsx +234 -0
  271. package/src/components/FallbackBoundary/ErrorBoundary.tsx +48 -0
  272. package/src/components/FallbackBoundary/Loading.tsx +14 -0
  273. package/src/components/FallbackBoundary/index.tsx +15 -0
  274. package/src/components/HistoryList.tsx +16 -0
  275. package/src/components/IconInput.tsx +70 -0
  276. package/src/components/Markdown.tsx +53 -0
  277. package/src/components/OverlayMenu.tsx +10 -0
  278. package/src/components/ProgressBar.tsx +153 -0
  279. package/src/components/QuickStartButton.tsx +51 -0
  280. package/src/components/RightPanelForm.tsx +55 -0
  281. package/src/components/RightPanelTabs.tsx +39 -0
  282. package/src/components/TabManager.tsx +223 -0
  283. package/src/components/Tooltip/Tooltip.tsx +30 -0
  284. package/src/components/Tooltip/TooltipAPI.ts +46 -0
  285. package/src/components/Tooltip/context.tsx +24 -0
  286. package/src/components/Tooltip/index.ts +2 -0
  287. package/src/components/Tooltip/style.tsx +25 -0
  288. package/src/components/Tooltip/types.ts +8 -0
  289. package/src/components/form/DescribedCheckboxGroup.tsx +39 -0
  290. package/src/components/form/DescribedRadioGroup.tsx +33 -0
  291. package/src/components/form/styled.ts +41 -0
  292. package/src/components/form/types.ts +21 -0
  293. package/src/context/AIWidgetProvider.tsx +6 -0
  294. package/src/context/hooks.ts +93 -0
  295. package/src/features.ts +18 -0
  296. package/src/hooks/chat-scroll.ts +14 -0
  297. package/src/index.ts +8 -0
  298. package/src/layout.css +119 -0
  299. package/src/right-panel/DefaultPanel.tsx +67 -0
  300. package/src/right-panel/RightPanel.tsx +6 -0
  301. package/src/right-panel/RightPanelProvider.tsx +20 -0
  302. package/src/right-panel/hooks.tsx +47 -0
  303. package/src/state/ChatEntry.ts +95 -0
  304. package/src/state/ChatState.ts +85 -0
  305. package/src/state/ChatTabsController.ts +55 -0
  306. package/src/state/ObservableState.ts +35 -0
  307. package/src/state/WidgetState.ts +42 -0
  308. package/src/types.ts +20 -0
  309. package/src/utils/chat.ts +30 -0
  310. package/src/utils/date.ts +40 -0
  311. package/src/views/Agents.tsx +1 -0
  312. package/src/views/Chat/AgentInfo.tsx +17 -0
  313. package/src/views/Chat/ChatMessage.tsx +89 -0
  314. package/src/views/Chat/ChatMessages.tsx +16 -0
  315. package/src/views/Chat/index.tsx +11 -0
  316. package/src/views/Chat/styled.ts +116 -0
  317. package/src/views/ChatTabSelection.tsx +65 -0
  318. package/src/views/Editor.tsx +1 -0
  319. package/src/views/Home.tsx +109 -0
  320. package/src/views/KnowledgeSources.tsx +115 -0
  321. package/src/views/MessageInput/ButtonGroup.tsx +84 -0
  322. package/src/views/MessageInput/InfoBar.tsx +69 -0
  323. package/src/views/MessageInput/dictionary.ts +36 -0
  324. package/src/views/MessageInput/index.tsx +79 -0
  325. package/src/views/MessageInput/styled.ts +196 -0
  326. package/src/views/MinimizedHeader.tsx +94 -0
  327. package/src/views/Stacks.tsx +104 -0
  328. package/src/views/Workspaces.tsx +88 -0
  329. package/tsconfig.json +22 -0
@@ -0,0 +1,93 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import { useContext, useEffect, useMemo, useState } from 'react'
3
+ import { ChatEntry } from '../state/ChatEntry'
4
+ import { ChatProperties, ChatState, MessageInterceptor } from '../state/ChatState'
5
+ import { ObservableState } from '../state/ObservableState'
6
+ import { WidgetProperties, WidgetState } from '../state/WidgetState'
7
+ import { createNewChat } from '../utils/chat'
8
+ import { AIWidgetContext } from './AIWidgetProvider'
9
+
10
+ let globalWidgetState: WidgetState | undefined
11
+
12
+ export function useWidget(): WidgetState {
13
+ const fromContext = useContext(AIWidgetContext)
14
+ if (fromContext) return fromContext
15
+ globalWidgetState ??= new WidgetState()
16
+ return globalWidgetState
17
+ }
18
+
19
+ function useObservableState<T, K extends keyof T>(state: ObservableState<T>, key: K): T[K] {
20
+ const [value, setValue] = useState(state.get(key))
21
+ useEffect(() => {
22
+ setValue(state.get(key))
23
+ return state.onChange(key, setValue)
24
+ }, [state])
25
+ return value
26
+ }
27
+
28
+ export function useWidgetState<K extends keyof WidgetProperties>(key: K): WidgetProperties[K] {
29
+ const widget = useWidget()
30
+ return useObservableState(widget, key)
31
+ }
32
+
33
+ export function useChatTabs(): { chats: ChatState[], active: string } {
34
+ const widget = useWidget()
35
+ const [tabs, setTabs] = useState<{ chats: ChatState[], active: string }>({
36
+ chats: widget.chatTabs.getAll(),
37
+ active: widget.chatTabs.getActiveChatId(),
38
+ })
39
+ useEffect(() => widget.chatTabs.onChange((chats, active) => setTabs({ chats, active })), [])
40
+ return tabs
41
+ }
42
+
43
+ export function useChat(chatId: string): ChatState {
44
+ const widget = useWidget()
45
+ const chat = widget.chatTabs.get(chatId)
46
+ if (!chat) throw new Error(`No chat with id ${chatId} was found. Maybe there are no chats opened, try to call useFirstChat first.`)
47
+ return chat
48
+ }
49
+
50
+ export function useCurrentChat(): ChatState {
51
+ const { active } = useChatTabs()
52
+ return useChat(active)
53
+ }
54
+
55
+ export function useChatState<K extends keyof ChatProperties>(chatId: string, key: K): ChatProperties[K] {
56
+ const chat = useChat(chatId)
57
+ return useObservableState(chat, key)
58
+ }
59
+
60
+ export function useCurrentChatState<K extends keyof ChatProperties>(key: K): ChatProperties[K] {
61
+ const chat = useCurrentChat()
62
+ return useObservableState(chat, key)
63
+ }
64
+
65
+ export function useChatMessages(chatId: string): ChatEntry[] {
66
+ const chat = useChat(chatId)
67
+ const [entries, setEntries] = useState(chat.getMessages())
68
+ useEffect(() => {
69
+ setEntries(chat.getMessages())
70
+ return chat.onChangeMessages(setEntries)
71
+ }, [chat])
72
+ return entries
73
+ }
74
+
75
+ export function useCurrentChatMessages(): ChatEntry[] {
76
+ const { active } = useChatTabs()
77
+ return useChatMessages(active)
78
+ }
79
+
80
+ export function useChatEntry(entry: ChatEntry) {
81
+ const immutable = useMemo(() => entry.hasFinished(), [])
82
+ // the following condition is not a problem for react hooks, it will always be true or always false.
83
+ if (immutable) return entry.getValue()
84
+ const [content, setContent] = useState(entry.getValue())
85
+ useEffect(() => entry.onChange(setContent), [])
86
+ return content
87
+ }
88
+
89
+ export function useFirstChat(interceptors: MessageInterceptor[]) {
90
+ const widget = useWidget()
91
+ const tabs = widget.chatTabs
92
+ if (!tabs.getAll().length) createNewChat(widget, interceptors)
93
+ }
@@ -0,0 +1,18 @@
1
+ export interface MessageInputFeatures {
2
+ stack?: boolean,
3
+ workspace?: boolean,
4
+ knowledgeSource?: boolean,
5
+ agent?: boolean,
6
+ quickCommands?: boolean,
7
+ }
8
+
9
+ export interface AIWidgetFeatures extends MessageInputFeatures {
10
+ editor?: boolean,
11
+ chatHistory?: boolean,
12
+ }
13
+
14
+ export const defaultFeatures: AIWidgetFeatures = {
15
+ stack: true,
16
+ workspace: true,
17
+ knowledgeSource: true,
18
+ }
@@ -0,0 +1,14 @@
1
+ import { useEffect } from 'react'
2
+
3
+ /**
4
+ * Scrolls the closest chat (upwards in the tree) to its bottom.
5
+ * @param ref the reference element.
6
+ * @param deps when the deps changes, the chat is scrolled.
7
+ */
8
+ export function useChatScrollToBottomEffect(ref: React.RefObject<HTMLElement>, deps: any[]) {
9
+ useEffect(() => {
10
+ const chat = ref.current?.closest('.chat-content')
11
+ if (!chat) return
12
+ chat.scrollTop = chat.scrollHeight
13
+ }, deps)
14
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export { AIWidgetProvider } from './context/AIWidgetProvider'
2
+ export * from './context/hooks'
3
+ export { StackspotAIWidget } from './StackspotAIWidget'
4
+ export { ChatEntry } from './state/ChatEntry'
5
+ export { ChatState } from './state/ChatState'
6
+ export { ChatTabsController } from './state/ChatTabsController'
7
+ export { ObservableState } from './state/ObservableState'
8
+ export { WidgetState } from './state/WidgetState'
package/src/layout.css ADDED
@@ -0,0 +1,119 @@
1
+ @font-face {
2
+ font-family: "San Francisco";
3
+ font-weight: 400;
4
+ src: url("https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-regular-webfont.woff");
5
+ }
6
+
7
+ /* Scroll bars */
8
+
9
+ ::-webkit-scrollbar-track {
10
+ background-color: transparent;
11
+ }
12
+
13
+ ::-webkit-scrollbar {
14
+ width: 0.25rem;
15
+ height: 0.5rem;
16
+ background-color: transparent;
17
+ }
18
+
19
+ ::-webkit-scrollbar-thumb {
20
+ background-color: var(--light-600);
21
+ }
22
+
23
+ ::-webkit-scrollbar-corner {
24
+ background-color: transparent;
25
+ }
26
+
27
+ /* Classes */
28
+
29
+ .ai-chat-widget {
30
+ border: 1px solid var(--light-600);
31
+ background-color: var(--light-400);
32
+ border-radius: 4px;
33
+ position: relative;
34
+ overflow: hidden;
35
+ width: 100%;
36
+ height: 100%;
37
+
38
+ &.minimized {
39
+ .chat-container {
40
+ padding: 10px;
41
+ width: calc(100% - 20px);
42
+ }
43
+ .minimized-header {
44
+ padding: 0 10px;
45
+ width: calc(100% - 20px);
46
+ }
47
+ .home-page {
48
+ .title {
49
+ margin-top: 40px;
50
+ font-size: 18px;
51
+ }
52
+ .subtitle {
53
+ font-size: 14px;
54
+ }
55
+ .title, .subtitle {
56
+ display: block;
57
+ text-align: center;
58
+ margin-left: 20px;
59
+ margin-right: 20px;
60
+ }
61
+ .shortcuts {
62
+ display: none;
63
+ }
64
+ }
65
+ .message-input .action-box{
66
+ .feature-buttons, .expand {
67
+ display: none;
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ .chat-window {
74
+ transition: width 0.3s;
75
+ width: 100%;
76
+ display: flex;
77
+ flex-direction: column;
78
+ align-items: center;
79
+ height: 100%;
80
+
81
+ &.narrow {
82
+ width: 50%;
83
+ }
84
+ }
85
+
86
+ .chat-container {
87
+ padding: 20px;
88
+ max-width: 1200px;
89
+ width: calc(100% - 40px);
90
+ display: flex;
91
+ flex-direction: column;
92
+ flex: 1;
93
+ }
94
+
95
+ .chat-content {
96
+ display: flex;
97
+ flex-direction: column;
98
+ flex: 1;
99
+ flex-basis: 0;
100
+ overflow: auto;
101
+ }
102
+
103
+ .chat-right-panel {
104
+ position: absolute;
105
+ width: 50%;
106
+ left: 100%;
107
+ top: 0;
108
+ bottom: 0;
109
+ transition: left 0.3s ease-in-out;
110
+ background-color: var(--light-300);
111
+ border-radius: 4px;
112
+ border-left: 2px solid var(--light-600);
113
+ display: flex;
114
+ flex-direction: column;
115
+
116
+ &.visible {
117
+ left: 50%;
118
+ }
119
+ }
@@ -0,0 +1,67 @@
1
+ import { Text } from '@citric/core'
2
+ import { Times } from '@citric/icons'
3
+ import { IconButton } from '@citric/ui'
4
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
5
+ import { styled } from 'styled-components'
6
+ import { WithChildren } from '../types'
7
+
8
+ interface Props extends WithChildren {
9
+ title: string,
10
+ description: string,
11
+ onClose: () => void,
12
+ }
13
+
14
+ const PanelBox = styled.div`
15
+ padding: 25px;
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 25px;
19
+ flex: 1;
20
+ overflow: hidden;
21
+
22
+ header {
23
+ display: flex;
24
+ flex-direction: row;
25
+
26
+ .title {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 10px;
30
+ flex: 1;
31
+ }
32
+ }
33
+
34
+ article {
35
+ flex: 1;
36
+ display: flex;
37
+ flex-direction: column;
38
+ overflow: auto;
39
+ }
40
+ `
41
+
42
+ export const DefaultPanel = ({ description, onClose, title, children }: Props) => {
43
+ const t = useTranslate(dictionary)
44
+ return (
45
+ <PanelBox>
46
+ <header>
47
+ <div className="title">
48
+ <Text appearance="h5">{title}</Text>
49
+ <Text colorScheme="light.700">{description}</Text>
50
+ </div>
51
+ <IconButton title={t.close} aria-label={t.close} onClick={onClose}>
52
+ <Times />
53
+ </IconButton>
54
+ </header>
55
+ <article>{children}</article>
56
+ </PanelBox>
57
+ )
58
+ }
59
+
60
+ const dictionary = {
61
+ en: {
62
+ close: 'Close',
63
+ },
64
+ pt: {
65
+ close: 'Fechar',
66
+ },
67
+ } satisfies Dictionary
@@ -0,0 +1,6 @@
1
+ import { useRightPanelContent } from './hooks'
2
+
3
+ export const RightPanel = () => {
4
+ const content = useRightPanelContent()
5
+ return content
6
+ }
@@ -0,0 +1,20 @@
1
+ import { createContext, useMemo, useRef, useState } from 'react'
2
+
3
+ interface ContextValue {
4
+ content?: React.ReactNode,
5
+ setContent?: (content: React.ReactNode) => void,
6
+ panel?: React.RefObject<HTMLDivElement>,
7
+ chatWindow?: React.RefObject<HTMLDivElement>,
8
+ onCloseNext: React.MutableRefObject<(() => void) | undefined>,
9
+ }
10
+
11
+ export const RightPanelContext = createContext<ContextValue>({ onCloseNext: { current: undefined } })
12
+
13
+ export const RightPanelProvider = (
14
+ { panel, chatWindow, children }: Pick<ContextValue, 'panel' | 'chatWindow'> & { children: React.ReactNode },
15
+ ) => {
16
+ const [content, setContent] = useState<React.ReactNode>()
17
+ const onCloseNext = useRef<(() => void) | undefined>()
18
+ const value = useMemo<ContextValue>(() => ({ content, setContent, panel, chatWindow, onCloseNext }), [content, panel, chatWindow])
19
+ return <RightPanelContext.Provider value={value}>{children}</RightPanelContext.Provider>
20
+ }
@@ -0,0 +1,47 @@
1
+ import { useContext, useMemo } from 'react'
2
+ import { DefaultPanel } from './DefaultPanel'
3
+ import { RightPanelContext } from './RightPanelProvider'
4
+
5
+ interface RightPanelOptions {
6
+ title: string,
7
+ description: string,
8
+ onClose?: () => void,
9
+ }
10
+
11
+ export function useRightPanelContent() {
12
+ const { content } = useContext(RightPanelContext)
13
+ return content
14
+ }
15
+
16
+ export function useRightPanel() {
17
+ const ctx = useContext(RightPanelContext)
18
+ const { panel, chatWindow, setContent } = ctx
19
+
20
+ return useMemo(() => {
21
+ if (!setContent) throw new Error('No RightPanelProvider found.')
22
+
23
+ function open(content: React.ReactNode, options?: RightPanelOptions) {
24
+ if (!panel?.current || !chatWindow?.current || !setContent) return
25
+ setContent(options ? <DefaultPanel {...options} onClose={close}>{content}</DefaultPanel> : content)
26
+ panel.current.classList.add('visible')
27
+ chatWindow.current.classList.add('narrow')
28
+ ctx.onCloseNext.current = options?.onClose
29
+ }
30
+
31
+ function close() {
32
+ if (!panel?.current || !chatWindow?.current || !setContent) return
33
+ panel.current.classList.remove('visible')
34
+ chatWindow.current.classList.remove('narrow')
35
+ ctx.onCloseNext.current?.()
36
+ setTimeout(() => {
37
+ setContent(null)
38
+ }, 300)
39
+ }
40
+
41
+ function isOpen() {
42
+ return !!panel?.current?.classList.contains('visible')
43
+ }
44
+
45
+ return { open, close, isOpen }
46
+ }, [])
47
+ }
@@ -0,0 +1,95 @@
1
+ import { pull } from 'lodash'
2
+
3
+ export interface SerializableAction {
4
+ title: string,
5
+ type: 'link' | 'command',
6
+ /**
7
+ * The URL if the action is a link or a chat command otherwise (/command).
8
+ */
9
+ exec: string,
10
+ }
11
+
12
+ export interface ChatAction extends SerializableAction {
13
+ /**
14
+ * @default primary
15
+ */
16
+ appearance?: 'primary' | 'secondary',
17
+ }
18
+
19
+ export interface TextChatEntry {
20
+ type: 'text' | 'md',
21
+ agent: 'bot' | 'user' | 'system',
22
+ // image?: string,
23
+ actions?: ChatAction[],
24
+ subtitle?: string,
25
+ content: string,
26
+ // knowledgeSources?: KnowledgeSource[],
27
+ updated?: string,
28
+ agentId?: string,
29
+ messageId?: string,
30
+ error?: string,
31
+ // customInput?: CustomInputResponse,
32
+ }
33
+
34
+ type ChatEntryListener = (value: TextChatEntry) => void
35
+
36
+ let nextId = 0
37
+
38
+ export class ChatEntry {
39
+ readonly id: number
40
+ private value: TextChatEntry
41
+ private streamFinished: boolean
42
+ private listeners: ChatEntryListener[] = []
43
+ abort: () => void
44
+
45
+ /**
46
+ * @param value the value of the entry.
47
+ * @param isStreamed whether or not this entry is streamed. Defaults to false.
48
+ * @param abort an abort function to cancel the transmission of this chat entry. Specially useful for canceling streamings.
49
+ */
50
+ constructor(value: TextChatEntry, isStreamed = false, abort = () => {}) {
51
+ this.id = nextId++
52
+ this.value = value
53
+ this.streamFinished = !isStreamed
54
+ this.abort = abort
55
+ }
56
+
57
+ static createUserEntry(content: string) {
58
+ return new ChatEntry({
59
+ agent: 'user',
60
+ type: 'text',
61
+ content,
62
+ updated: new Date().toISOString(),
63
+ })
64
+ }
65
+
66
+ static createStreamedBotEntry(abort: () => void) {
67
+ return new ChatEntry({ agent: 'bot', type: 'md', content: '' }, true, abort)
68
+ }
69
+
70
+ setValue(value: TextChatEntry) {
71
+ if (this.streamFinished) return
72
+ this.value = value
73
+ this.listeners.forEach(l => l(this.value))
74
+ }
75
+
76
+ getValue() {
77
+ return this.value
78
+ }
79
+
80
+ finish() {
81
+ this.streamFinished = true
82
+ this.listeners = []
83
+ }
84
+
85
+ hasFinished() {
86
+ return this.streamFinished
87
+ }
88
+
89
+ onChange(listener: ChatEntryListener) {
90
+ if (!this.streamFinished) this.listeners.push(listener)
91
+ return () => {
92
+ pull(this.listeners, listener)
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,85 @@
1
+ import { dropRight, last, pull } from 'lodash'
2
+ import { ChatEntry } from './ChatEntry'
3
+ import { ObservableState } from './ObservableState'
4
+
5
+ interface Labeled {
6
+ id: string,
7
+ label: string,
8
+ }
9
+
10
+ export interface ChatProperties {
11
+ label: string,
12
+ agent?: Labeled,
13
+ workspace?: Labeled,
14
+ stack?: Labeled,
15
+ knowledgeSources?: Labeled[],
16
+ isLoading?: boolean,
17
+ nextMessage?: string,
18
+ }
19
+
20
+ type ChatMessagesListener = (chat: ChatEntry[]) => void
21
+
22
+ export type MessageInterceptor = (entry: ChatEntry, chat: ChatState) => boolean | undefined | void | Promise<boolean | undefined | void>
23
+
24
+ interface Options {
25
+ id: string,
26
+ initial: ChatProperties,
27
+ interceptors?: MessageInterceptor[],
28
+ entries?: ChatEntry[],
29
+ }
30
+
31
+ export class ChatState extends ObservableState<ChatProperties> {
32
+ readonly id: string
33
+ private entries: ChatEntry[]
34
+ private messagesListeners: ChatMessagesListener[] = []
35
+ private readonly interceptors: MessageInterceptor[]
36
+
37
+ /**
38
+ * @param id the id of the chat.
39
+ * @param initial the initial state.
40
+ * @param interceptors a list of interceptors to run whenever a new message (entry) is added to the chat. If an interception function
41
+ * returns false, the next interceptors are not run. Attention: when multiple messages are added at once, only the last goes through the
42
+ * interceptors. Furthermore, messages created by the constructor don't go through the interceptors, only messages added via `pushMessage`
43
+ * do.
44
+ */
45
+ constructor({ id, initial, entries = [], interceptors = [] }: Options) {
46
+ super(initial)
47
+ this.id = id
48
+ this.interceptors = interceptors
49
+ this.entries = entries
50
+ }
51
+
52
+ private runMessagesListeners() {
53
+ this.messagesListeners.forEach(l => l(this.entries))
54
+ }
55
+
56
+ private async runInterceptors(entry: ChatEntry) {
57
+ for (const interceptor of this.interceptors) {
58
+ const result = await interceptor(entry, this)
59
+ if (result === false) break
60
+ }
61
+ }
62
+
63
+ pushMessage(...entries: ChatEntry[]) {
64
+ if (!entries.length) return
65
+ this.entries = [...this.entries, ...entries]
66
+ this.runMessagesListeners()
67
+ this.runInterceptors(last(entries)!)
68
+ }
69
+
70
+ popMessage(quantity = 1) {
71
+ this.entries = dropRight(this.entries, quantity)
72
+ this.runMessagesListeners()
73
+ }
74
+
75
+ getMessages() {
76
+ return this.entries
77
+ }
78
+
79
+ onChangeMessages(listener: ChatMessagesListener) {
80
+ this.messagesListeners.push(listener)
81
+ return () => {
82
+ pull(this.messagesListeners, listener)
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,55 @@
1
+ import { last, pull } from 'lodash'
2
+ import { ChatState } from './ChatState'
3
+
4
+ type TabChangeListener = (chats: ChatState[], activeId: string) => void
5
+
6
+ export class ChatTabsController {
7
+ private chats: ChatState[] = []
8
+ private activeChatId = ''
9
+ private listeners: TabChangeListener[] = []
10
+
11
+ private runListeners() {
12
+ this.listeners.forEach(l => l(this.chats, this.activeChatId))
13
+ }
14
+
15
+ add(...chats: ChatState[]) {
16
+ if (!chats.length) return
17
+ this.chats = [...this.chats, ...chats]
18
+ this.runListeners()
19
+ }
20
+
21
+ remove(...ids: string[]) {
22
+ if (this.chats.length <= 1 || !ids.length) return
23
+ const currentActiveIndex = this.chats.findIndex(c => c.id === this.activeChatId)
24
+ this.chats = this.chats.filter(c => !ids.includes(c.id))
25
+ if (ids.includes(this.activeChatId)) {
26
+ if (currentActiveIndex === -1) this.activeChatId = this.chats[0]?.id
27
+ this.activeChatId = currentActiveIndex < this.chats.length ? this.chats[currentActiveIndex].id : last(this.chats)?.id ?? ''
28
+ }
29
+ this.runListeners()
30
+ }
31
+
32
+ get(id: string) {
33
+ return this.chats.find(c => c.id === id)
34
+ }
35
+
36
+ getActiveChatId() {
37
+ return this.activeChatId
38
+ }
39
+
40
+ getAll() {
41
+ return this.chats
42
+ }
43
+
44
+ onChange(listener: TabChangeListener) {
45
+ this.listeners.push(listener)
46
+ return () => {
47
+ pull(this.listeners, listener)
48
+ }
49
+ }
50
+
51
+ select(id: string) {
52
+ this.activeChatId = id
53
+ this.runListeners()
54
+ }
55
+ }
@@ -0,0 +1,35 @@
1
+ import { pull } from 'lodash'
2
+
3
+ export type ObservableStateListener<T, K extends keyof T> = (value: T[K]) => void
4
+
5
+ type Listeners<T> = {
6
+ [K in keyof T]?: ((value: T[K]) => void)[]
7
+ }
8
+
9
+ export class ObservableState<T> {
10
+ private state: T
11
+ private listeners: Listeners<T> = {}
12
+
13
+ constructor(initial: T) {
14
+ this.state = { ...initial }
15
+ }
16
+
17
+ set<K extends keyof T>(key: K, value: T[K]) {
18
+ if (this.state[key] !== value) {
19
+ this.state[key] = value
20
+ this.listeners[key]?.forEach(l => l(value))
21
+ }
22
+ }
23
+
24
+ get<K extends keyof T>(key: K): T[K] {
25
+ return this.state[key]
26
+ }
27
+
28
+ onChange<K extends keyof T>(key: K, listener: (value: T[K]) => void) {
29
+ this.listeners[key] ??= []
30
+ this.listeners[key]!.push(listener)
31
+ return () => {
32
+ pull(this.listeners[key]!, listener)
33
+ }
34
+ }
35
+ }