@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,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