@stack-spot/ai-chat-widget 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/StackspotAIWidget.d.ts +1 -12
  3. package/dist/StackspotAIWidget.d.ts.map +1 -1
  4. package/dist/StackspotAIWidget.js +5 -18
  5. package/dist/StackspotAIWidget.js.map +1 -1
  6. package/dist/chat-interceptors/quick-commands.js +1 -1
  7. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  8. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  9. package/dist/chat-interceptors/send-message.js +21 -2
  10. package/dist/chat-interceptors/send-message.js.map +1 -1
  11. package/dist/components/AutoFocus.d.ts.map +1 -1
  12. package/dist/components/AutoFocus.js +8 -1
  13. package/dist/components/AutoFocus.js.map +1 -1
  14. package/dist/components/FadingOverflow.js +2 -2
  15. package/dist/components/FadingOverflow.js.map +1 -1
  16. package/dist/components/QuickStartButton.d.ts +6 -1
  17. package/dist/components/QuickStartButton.d.ts.map +1 -1
  18. package/dist/components/QuickStartButton.js +6 -2
  19. package/dist/components/QuickStartButton.js.map +1 -1
  20. package/dist/components/RightPanelForm.d.ts.map +1 -1
  21. package/dist/components/RightPanelForm.js +2 -1
  22. package/dist/components/RightPanelForm.js.map +1 -1
  23. package/dist/context/hooks.d.ts +2 -7
  24. package/dist/context/hooks.d.ts.map +1 -1
  25. package/dist/context/hooks.js +4 -16
  26. package/dist/context/hooks.js.map +1 -1
  27. package/dist/features.d.ts +16 -17
  28. package/dist/features.d.ts.map +1 -1
  29. package/dist/features.js +17 -9
  30. package/dist/features.js.map +1 -1
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/layout.css +7 -0
  36. package/dist/state/ChatState.d.ts +23 -3
  37. package/dist/state/ChatState.d.ts.map +1 -1
  38. package/dist/state/ChatState.js +5 -2
  39. package/dist/state/ChatState.js.map +1 -1
  40. package/dist/state/ChatTabsController.d.ts +21 -3
  41. package/dist/state/ChatTabsController.d.ts.map +1 -1
  42. package/dist/state/ChatTabsController.js +49 -11
  43. package/dist/state/ChatTabsController.js.map +1 -1
  44. package/dist/state/WidgetState.d.ts +50 -1
  45. package/dist/state/WidgetState.d.ts.map +1 -1
  46. package/dist/state/WidgetState.js +55 -2
  47. package/dist/state/WidgetState.js.map +1 -1
  48. package/dist/utils/chat.d.ts +1 -8
  49. package/dist/utils/chat.d.ts.map +1 -1
  50. package/dist/utils/chat.js +0 -15
  51. package/dist/utils/chat.js.map +1 -1
  52. package/dist/views/Agents/AgentDescription.d.ts +9 -0
  53. package/dist/views/Agents/AgentDescription.d.ts.map +1 -0
  54. package/dist/views/Agents/AgentDescription.js +21 -0
  55. package/dist/views/Agents/AgentDescription.js.map +1 -0
  56. package/dist/views/Agents/AgentsPanel.d.ts +5 -0
  57. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -0
  58. package/dist/views/Agents/AgentsPanel.js +19 -0
  59. package/dist/views/Agents/AgentsPanel.js.map +1 -0
  60. package/dist/views/Agents/AgentsTab.d.ts +5 -0
  61. package/dist/views/Agents/AgentsTab.d.ts.map +1 -0
  62. package/dist/views/Agents/AgentsTab.js +43 -0
  63. package/dist/views/Agents/AgentsTab.js.map +1 -0
  64. package/dist/views/Agents/dictionary.d.ts +2 -0
  65. package/dist/views/Agents/dictionary.d.ts.map +1 -0
  66. package/dist/views/Agents/dictionary.js +35 -0
  67. package/dist/views/Agents/dictionary.js.map +1 -0
  68. package/dist/views/Agents/index.d.ts +5 -0
  69. package/dist/views/Agents/index.d.ts.map +1 -0
  70. package/dist/views/Agents/index.js +21 -0
  71. package/dist/views/Agents/index.js.map +1 -0
  72. package/dist/views/Agents/styled.d.ts +3 -0
  73. package/dist/views/Agents/styled.d.ts.map +1 -0
  74. package/dist/views/Agents/styled.js +58 -0
  75. package/dist/views/Agents/styled.js.map +1 -0
  76. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  77. package/dist/views/Chat/ChatMessage.js +1 -1
  78. package/dist/views/Chat/ChatMessage.js.map +1 -1
  79. package/dist/views/Chat/index.js +1 -1
  80. package/dist/views/Chat/index.js.map +1 -1
  81. package/dist/views/Chat/styled.js +1 -1
  82. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts +1 -4
  83. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts.map +1 -1
  84. package/dist/views/ChatHistory/ChatHistoryPanel.js +2 -2
  85. package/dist/views/ChatHistory/ChatHistoryPanel.js.map +1 -1
  86. package/dist/views/ChatHistory/HistoryItem.d.ts +1 -3
  87. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
  88. package/dist/views/ChatHistory/HistoryItem.js +2 -2
  89. package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
  90. package/dist/views/ChatHistory/index.d.ts +1 -9
  91. package/dist/views/ChatHistory/index.d.ts.map +1 -1
  92. package/dist/views/ChatHistory/index.js +2 -2
  93. package/dist/views/ChatHistory/index.js.map +1 -1
  94. package/dist/views/ChatTabSelection.d.ts +1 -7
  95. package/dist/views/ChatTabSelection.d.ts.map +1 -1
  96. package/dist/views/ChatTabSelection.js +7 -7
  97. package/dist/views/ChatTabSelection.js.map +1 -1
  98. package/dist/views/Editor.d.ts.map +1 -1
  99. package/dist/views/Editor.js +4 -1
  100. package/dist/views/Editor.js.map +1 -1
  101. package/dist/views/Home/BuiltInAgent.d.ts +6 -0
  102. package/dist/views/Home/BuiltInAgent.d.ts.map +1 -0
  103. package/dist/views/{Home.js → Home/BuiltInAgent.js} +7 -41
  104. package/dist/views/Home/BuiltInAgent.js.map +1 -0
  105. package/dist/views/Home/CustomAgent.d.ts +5 -0
  106. package/dist/views/Home/CustomAgent.d.ts.map +1 -0
  107. package/dist/views/Home/CustomAgent.js +24 -0
  108. package/dist/views/Home/CustomAgent.js.map +1 -0
  109. package/dist/views/Home/index.d.ts +8 -0
  110. package/dist/views/Home/index.d.ts.map +1 -0
  111. package/dist/views/Home/index.js +15 -0
  112. package/dist/views/Home/index.js.map +1 -0
  113. package/dist/views/Home/styled.d.ts +2 -0
  114. package/dist/views/Home/styled.d.ts.map +1 -0
  115. package/dist/views/Home/styled.js +59 -0
  116. package/dist/views/Home/styled.js.map +1 -0
  117. package/dist/views/Home/types.d.ts +7 -0
  118. package/dist/views/Home/types.d.ts.map +1 -0
  119. package/dist/views/Home/types.js +2 -0
  120. package/dist/views/Home/types.js.map +1 -0
  121. package/dist/views/KnowledgeSources.js +1 -1
  122. package/dist/views/KnowledgeSources.js.map +1 -1
  123. package/dist/views/MessageInput/ButtonGroup.d.ts +1 -6
  124. package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
  125. package/dist/views/MessageInput/ButtonGroup.js +12 -4
  126. package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
  127. package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
  128. package/dist/views/MessageInput/InfoBar.js +16 -6
  129. package/dist/views/MessageInput/InfoBar.js.map +1 -1
  130. package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
  131. package/dist/views/MessageInput/QuickCommandSelector.js +2 -1
  132. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
  133. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  134. package/dist/views/MessageInput/index.d.ts +1 -9
  135. package/dist/views/MessageInput/index.d.ts.map +1 -1
  136. package/dist/views/MessageInput/index.js +2 -2
  137. package/dist/views/MessageInput/index.js.map +1 -1
  138. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  139. package/dist/views/MessageInput/styled.js +6 -2
  140. package/dist/views/MessageInput/styled.js.map +1 -1
  141. package/dist/views/MinimizedHeader.d.ts.map +1 -1
  142. package/dist/views/MinimizedHeader.js +2 -3
  143. package/dist/views/MinimizedHeader.js.map +1 -1
  144. package/dist/views/Stacks.js +2 -1
  145. package/dist/views/Stacks.js.map +1 -1
  146. package/dist/views/Workspaces.js +2 -1
  147. package/dist/views/Workspaces.js.map +1 -1
  148. package/package.json +2 -2
  149. package/src/StackspotAIWidget.tsx +6 -32
  150. package/src/chat-interceptors/quick-commands.ts +1 -1
  151. package/src/chat-interceptors/send-message.ts +22 -2
  152. package/src/components/AutoFocus.tsx +9 -1
  153. package/src/components/FadingOverflow.tsx +2 -2
  154. package/src/components/QuickStartButton.tsx +17 -5
  155. package/src/components/RightPanelForm.tsx +2 -1
  156. package/src/context/hooks.ts +8 -20
  157. package/src/features.ts +27 -24
  158. package/src/index.ts +6 -0
  159. package/src/layout.css +7 -0
  160. package/src/state/ChatState.ts +26 -4
  161. package/src/state/ChatTabsController.ts +50 -11
  162. package/src/state/WidgetState.ts +88 -2
  163. package/src/utils/chat.ts +1 -18
  164. package/src/views/Agents/AgentDescription.tsx +48 -0
  165. package/src/views/Agents/AgentsPanel.tsx +19 -0
  166. package/src/views/Agents/AgentsTab.tsx +80 -0
  167. package/src/views/Agents/dictionary.ts +36 -0
  168. package/src/views/Agents/index.tsx +26 -0
  169. package/src/views/Agents/styled.ts +59 -0
  170. package/src/views/Chat/ChatMessage.tsx +19 -17
  171. package/src/views/Chat/index.tsx +1 -1
  172. package/src/views/Chat/styled.ts +1 -1
  173. package/src/views/ChatHistory/ChatHistoryPanel.tsx +2 -3
  174. package/src/views/ChatHistory/HistoryItem.tsx +3 -3
  175. package/src/views/ChatHistory/index.tsx +2 -10
  176. package/src/views/ChatTabSelection.tsx +8 -13
  177. package/src/views/Editor.tsx +4 -1
  178. package/src/views/{Home.tsx → Home/BuiltInAgent.tsx} +7 -48
  179. package/src/views/Home/CustomAgent.tsx +39 -0
  180. package/src/views/Home/index.tsx +20 -0
  181. package/src/views/Home/styled.ts +59 -0
  182. package/src/views/Home/types.ts +6 -0
  183. package/src/views/KnowledgeSources.tsx +2 -2
  184. package/src/views/MessageInput/ButtonGroup.tsx +15 -12
  185. package/src/views/MessageInput/InfoBar.tsx +25 -9
  186. package/src/views/MessageInput/QuickCommandSelector.tsx +2 -1
  187. package/src/views/MessageInput/index.tsx +1 -10
  188. package/src/views/MessageInput/styled.ts +6 -2
  189. package/src/views/MinimizedHeader.tsx +2 -3
  190. package/src/views/Stacks.tsx +3 -2
  191. package/src/views/Workspaces.tsx +3 -2
  192. package/dist/views/Agents.d.ts +0 -2
  193. package/dist/views/Agents.d.ts.map +0 -1
  194. package/dist/views/Agents.js +0 -146
  195. package/dist/views/Agents.js.map +0 -1
  196. package/dist/views/Home.d.ts +0 -14
  197. package/dist/views/Home.d.ts.map +0 -1
  198. package/dist/views/Home.js.map +0 -1
  199. package/src/views/Agents.tsx +0 -203
@@ -2,15 +2,8 @@ import { Clock, Plus } from '@citric/icons'
2
2
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
3
3
  import { useMemo } from 'react'
4
4
  import { TabManager } from '../components/TabManager'
5
- import { useChatState, useChatTabs, useWidget } from '../context/hooks'
6
- import { MessageInterceptor } from '../state/ChatState'
5
+ import { useChatState, useChatTabs, useWidget, useWidgetState } from '../context/hooks'
7
6
  import { ButtonAction } from '../types'
8
- import { createNewChat } from '../utils/chat'
9
-
10
- interface Props {
11
- history?: boolean,
12
- interceptors: MessageInterceptor[],
13
- }
14
7
 
15
8
  const TabLabel = ({ id }: { id: string }) => {
16
9
  const label = useChatState(id, 'label')
@@ -20,9 +13,10 @@ const TabLabel = ({ id }: { id: string }) => {
20
13
  /**
21
14
  * This renders the top-most part of the layout, which includes the chat selection through tabs.
22
15
  */
23
- export const ChatTabSelection = ({ history, interceptors }: Props) => {
16
+ export const ChatTabSelection = () => {
24
17
  const t = useTranslate(dictionary)
25
18
  const widget = useWidget()
19
+ const { chatHistory } = useWidgetState('features') ?? {}
26
20
  const { active, chats } = useChatTabs()
27
21
 
28
22
  const buttons = useMemo<ButtonAction[]>(
@@ -30,9 +24,9 @@ export const ChatTabSelection = ({ history, interceptors }: Props) => {
30
24
  const actions: ButtonAction[] = [{
31
25
  icon: <Plus />,
32
26
  label: t.newChat,
33
- onClick: () => createNewChat(widget, interceptors),
27
+ onClick: () => widget.createChat(),
34
28
  }]
35
- if (history) {
29
+ if (chatHistory) {
36
30
  actions.push({
37
31
  icon: <Clock />,
38
32
  label: t.openHistory,
@@ -43,11 +37,12 @@ export const ChatTabSelection = ({ history, interceptors }: Props) => {
43
37
  }
44
38
  return actions
45
39
  },
46
- [history],
40
+ [chatHistory],
47
41
  )
42
+
48
43
  return <TabManager
49
44
  tabs={chats}
50
- active={active}
45
+ active={active.id}
51
46
  renderLabel={({ id }) => <TabLabel id={id} />}
52
47
  keygen={({ id }) => id}
53
48
  onRemove={({ id }) => widget.chatTabs.remove(id)}
@@ -1,6 +1,7 @@
1
1
  import { Text } from '@citric/core'
2
2
  import { LoadingCircular } from '@citric/ui'
3
3
  import MonacoEditor, { OnMount } from '@monaco-editor/react'
4
+ import { delay } from '@stack-spot/portal-components'
4
5
  import { Select } from '@stack-spot/portal-components/Select'
5
6
  import { useThemeKind } from '@stack-spot/portal-theme'
6
7
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
@@ -9,6 +10,7 @@ import { IDisposable } from 'monaco-editor'
9
10
  import { useCallback, useEffect, useMemo, useRef } from 'react'
10
11
  import { styled } from 'styled-components'
11
12
  import { useCurrentChat, useCurrentChatState, useWidget, useWidgetState } from '../context/hooks'
13
+ import { panelAnimationTime } from '../right-panel/constants'
12
14
  import { useRightPanel } from '../right-panel/hooks'
13
15
  import { defaultLanguage, languages } from '../utils/programming-languages'
14
16
 
@@ -92,11 +94,12 @@ const EditorPanel = () => {
92
94
  const chat = useCurrentChat()
93
95
  const selectionObserver = useRef<IDisposable | undefined>()
94
96
 
95
- const setup: OnMount = useCallback((editor) => {
97
+ const setup: OnMount = useCallback(async (editor) => {
96
98
  selectionObserver.current = editor.onDidChangeCursorSelection(debounce((e) => {
97
99
  const selectedText = editor.getModel()?.getValueInRange(e.selection)
98
100
  chat.set('codeSelection', selectedText?.trim() ? selectedText : undefined)
99
101
  }, MIN_SELECTION_UPDATE_MS))
102
+ await delay(panelAnimationTime)
100
103
  editor.focus()
101
104
  }, [])
102
105
 
@@ -2,57 +2,16 @@ import { FaceSmile, KnowledgeSource, QuickCommand } from '@citric/icons'
2
2
  import { MiniLogo } from '@stack-spot/portal-components/svg'
3
3
  import { theme } from '@stack-spot/portal-theme'
4
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
- /**
12
- * The name of the user currently logged in.
13
- */
14
- username: string,
15
- }
16
-
17
- const HomeBox = styled.div`
18
- margin: auto;
19
-
20
- .title, .subtitle {
21
- font-family: 'San Francisco';
22
- font-size: 26px;
23
- font-weight: 600;
24
- margin: 0;
25
- }
26
-
27
- .title {
28
- display: inline-block;
29
- background: linear-gradient(72.81deg, #FF9900 0.96%, #FF6633 100%);
30
- background-clip: text;
31
- margin-bottom: 10px;
32
- color: transparent;
33
- }
34
-
35
- .subtitle {
36
- color: #A0A0A0;
37
- margin-bottom: 20px;
38
- }
39
-
40
- .shortcuts {
41
- display: flex;
42
- flex-direction: row;
43
- gap: 15px;
44
- li {
45
- flex: 1;
46
- }
47
- }
48
- `
5
+ import { QuickStartButton } from '../../components/QuickStartButton'
6
+ import { useCurrentChat } from '../../context/hooks'
7
+ import { ChatEntry } from '../../state/ChatEntry'
8
+ import { HomeBox } from './styled'
9
+ import { HomeProps } from './types'
49
10
 
50
11
  /**
51
- * Renders the default home page for the chat. This shows up when no message has been sent yet.
52
- *
53
- * The home page can be replaced by providing children to the component `StackspotAIWidget`.
12
+ * This is the home rendered when the agent is built-in.
54
13
  */
55
- export const Home = ({ username }: Props) => {
14
+ export const BuiltInAgent = ({ username }: HomeProps) => {
56
15
  const t = useTranslate(dictionary)
57
16
  const chat = useCurrentChat()
58
17
 
@@ -0,0 +1,39 @@
1
+ import { Text } from '@citric/core'
2
+ import { MiniLogo } from '@stack-spot/portal-components/svg'
3
+ import { agentClient } from '@stack-spot/portal-network'
4
+ import { theme } from '@stack-spot/portal-theme'
5
+ import { useMemo } from 'react'
6
+ import { QuickStartButton } from '../../components/QuickStartButton'
7
+ import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
8
+ import { ChatEntry } from '../../state/ChatEntry'
9
+ import { HomeBox } from './styled'
10
+
11
+ /**
12
+ * This is the home rendered when the agent is custom.
13
+ */
14
+ export const CustomAgent = () => {
15
+ const { id, label, image } = useCurrentChatState('agent') ?? {}
16
+ const [agent] = agentClient.agent.useStatefulQuery({ agentId: id! })
17
+ const chat = useCurrentChat()
18
+ const suggestions = useMemo(() => agent?.suggested_prompts?.map((prompt, index) => (
19
+ <QuickStartButton
20
+ key={index}
21
+ label={prompt}
22
+ onClick={() => send(prompt)}
23
+ background={theme.color.light[500]}
24
+ manageOverflow
25
+ />
26
+ )), [agent?.suggested_prompts])
27
+
28
+ function send(message: string) {
29
+ chat.pushMessage(ChatEntry.createUserEntry(message))
30
+ }
31
+
32
+ return (
33
+ <HomeBox className="home-page custom-agent">
34
+ {image ? <img src={image} className="avatar" /> : <MiniLogo className="avatar" />}
35
+ <Text appearance="h3">{label}</Text>
36
+ <div className="shortcuts">{suggestions?.length ? suggestions : null}</div>
37
+ </HomeBox>
38
+ )
39
+ }
@@ -0,0 +1,20 @@
1
+ import { FallbackBoundary } from '../../components/FallbackBoundary'
2
+ import { useCurrentChatState } from '../../context/hooks'
3
+ import { BuiltInAgent } from './BuiltInAgent'
4
+ import { CustomAgent } from './CustomAgent'
5
+ import { HomeProps } from './types'
6
+
7
+ /**
8
+ * Renders the default home page for the chat. This shows up when no message has been sent yet.
9
+ *
10
+ * The home page can be replaced by providing children to the component `StackspotAIWidget`.
11
+ */
12
+ export const Home = (props: HomeProps) => {
13
+ const agent = useCurrentChatState('agent')
14
+
15
+ return (
16
+ <FallbackBoundary>
17
+ {agent && !agent.builtIn ? <CustomAgent /> : <BuiltInAgent {...props} />}
18
+ </FallbackBoundary>
19
+ )
20
+ }
@@ -0,0 +1,59 @@
1
+ import { styled } from 'styled-components'
2
+
3
+ export const HomeBox = styled.div`
4
+ margin: auto;
5
+
6
+ .title, .subtitle {
7
+ font-family: 'San Francisco';
8
+ font-size: 26px;
9
+ font-weight: 600;
10
+ margin: 0;
11
+ }
12
+
13
+ .title {
14
+ display: inline-block;
15
+ background: linear-gradient(72.81deg, #FF9900 0.96%, #FF6633 100%);
16
+ background-clip: text;
17
+ margin-bottom: 10px;
18
+ color: transparent;
19
+ }
20
+
21
+ .subtitle {
22
+ color: #A0A0A0;
23
+ margin-bottom: 20px;
24
+ }
25
+
26
+ .shortcuts {
27
+ display: flex;
28
+ flex-direction: row;
29
+ gap: 15px;
30
+ li {
31
+ flex: 1;
32
+ }
33
+ }
34
+
35
+ .avatar {
36
+ width: 74px;
37
+ height: 74px;
38
+ border-radius: 50%;
39
+ }
40
+
41
+ &.custom-agent {
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ gap: 20px;
46
+
47
+ .shortcuts {
48
+ margin-top: 10px;
49
+ button {
50
+ padding: 16px;
51
+ height: 100px;
52
+ line-height: 24px;
53
+ p {
54
+ overflow: hidden;
55
+ }
56
+ }
57
+ }
58
+ }
59
+ `
@@ -0,0 +1,6 @@
1
+ export interface HomeProps {
2
+ /**
3
+ * The name of the user currently logged in.
4
+ */
5
+ username: string,
6
+ }
@@ -105,9 +105,9 @@ const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit }: TabProps) => {
105
105
  {!!knowledgeSources.length && !filtered.length && (
106
106
  <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />
107
107
  )}
108
- {!knowledgeSources.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
108
+ {!knowledgeSources.length && <Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
109
109
  </div>
110
- <Button onClick={onSubmit}>{t.apply}</Button>
110
+ {!!filtered.length && <Button onClick={onSubmit}>{t.apply}</Button>}
111
111
  </>
112
112
  )
113
113
  }
@@ -4,14 +4,9 @@ import { MiniLogo } from '@stack-spot/portal-components/svg'
4
4
  import { listToClass } from '@stack-spot/portal-theme'
5
5
  import { useEffect, useRef } from 'react'
6
6
  import { useCurrentChatState, useWidget } from '../../context/hooks'
7
- import { MessageInputFeatures } from '../../features'
8
7
  import { useMessageInputDictionary } from './dictionary'
9
8
 
10
9
  interface ButtonGroupProps {
11
- /**
12
- * The features enabled and accessible through the message input.
13
- */
14
- features: MessageInputFeatures,
15
10
  /**
16
11
  * Whether or not the button group is expanded.
17
12
  */
@@ -38,19 +33,27 @@ interface ButtonGroupProps {
38
33
  * Renders the button group at right bottom side of the message input. This includes the send button as well as the buttons to open the
39
34
  * editor, change the agent, the stack, etc.
40
35
  */
41
- export const ButtonGroup = ({ features, onSend, onCancel, expanded, setExpanded, isLoading }: ButtonGroupProps) => {
36
+ export const ButtonGroup = ({ onSend, onCancel, expanded, setExpanded, isLoading }: ButtonGroupProps) => {
42
37
  const t = useMessageInputDictionary()
43
38
  const widget = useWidget()
44
39
  const featureButtonsWidth = useRef<number | undefined>()
45
40
  const featureButtons = useRef<HTMLDivElement>(null)
46
41
  const agent = useCurrentChatState('agent')
47
- const hasFeatureButtons = features.agent || features.workspace || features.knowledgeSource || features.stack
42
+ const features = useCurrentChatState('features')
43
+ const hasFeatureButtons = features.agent || features.workspace || features.knowledgeSource || features.stack || features.editor
48
44
 
49
- useEffect(() => {
50
- if (! featureButtons.current) return
51
- featureButtonsWidth.current = featureButtons.current.clientWidth
52
- featureButtons.current.style.width = `${featureButtonsWidth.current}px`
53
- }, [])
45
+ useEffect(
46
+ () => {
47
+ if (!featureButtons.current) return
48
+ const isHidden = featureButtons.current.style.width === '0px'
49
+ featureButtons.current.style.width = 'auto'
50
+ featureButtonsWidth.current = featureButtons.current.clientWidth
51
+ if (isHidden) featureButtons.current.style.width = '0px'
52
+ else featureButtons.current.style.width = `${featureButtonsWidth.current}px`
53
+ },
54
+ // don't use the whole features object here, it would make every chat tab change rerun this effect.
55
+ [features.agent, features.workspace, features.knowledgeSource, features.stack, features.editor],
56
+ )
54
57
 
55
58
  return (
56
59
  <div className="button-group">
@@ -11,13 +11,13 @@ interface InfoBadgeProps {
11
11
  label: string,
12
12
  color: ColorPaletteName,
13
13
  dismiss: string,
14
- onDismiss: () => void,
14
+ onDismiss?: () => void,
15
15
  }
16
16
 
17
17
  const InfoBadge = ({ label, color, dismiss, onDismiss }: InfoBadgeProps) => (
18
18
  <Badge appearance="square" palette={color} className="info-badge">
19
19
  {label}
20
- <IconButton onClick={onDismiss} title={dismiss} arial-label={dismiss}><TimesMini /></IconButton>
20
+ {onDismiss && <IconButton onClick={onDismiss} title={dismiss} arial-label={dismiss}><TimesMini /></IconButton>}
21
21
  </Badge>
22
22
  )
23
23
 
@@ -35,16 +35,25 @@ export const InfoBar = () => {
35
35
  const currentWorkspace = useCurrentChatState('workspace')
36
36
  const currentKnowledgeSources = useCurrentChatState('knowledgeSources')
37
37
  const currentSelection = useCurrentChatState('codeSelection')
38
+ const features = useCurrentChatState('features')
38
39
  const visible = !!(currentStack || currentWorkspace || currentKnowledgeSources?.length || currentSelection)
39
40
  const ksToRender = useMemo(() => currentKnowledgeSources?.map(ks => {
40
- const onDismiss = () => chat.set('knowledgeSources', currentKnowledgeSources.filter(({ id }) => id !== ks.id))
41
+ const onDismiss = features.knowledgeSource
42
+ ? (() => chat.set('knowledgeSources', currentKnowledgeSources.filter(({ id }) => id !== ks.id)))
43
+ : undefined
41
44
  return <li key={ks.id}><InfoBadge label={ks.label} dismiss={t.removeKS} color="teal" onDismiss={onDismiss} /></li>
42
45
  }), [currentKnowledgeSources])
46
+ const shouldRenderRemoveAllButton = (
47
+ currentSelection
48
+ || (features.stack && currentStack)
49
+ || (features.workspace && currentWorkspace)
50
+ || (features.knowledgeSource && !!ksToRender?.length)
51
+ )
43
52
 
44
53
  const removeAll = useCallback(() => {
45
- chat.set('knowledgeSources', [])
46
- chat.set('stack', undefined)
47
- chat.set('workspace', undefined)
54
+ if (features.knowledgeSource) chat.set('knowledgeSources', [])
55
+ if (features.stack) chat.set('stack', undefined)
56
+ if (features.workspace) chat.set('workspace', undefined)
48
57
  removeCodeSelection()
49
58
  }, [])
50
59
 
@@ -57,7 +66,9 @@ export const InfoBar = () => {
57
66
  <div className={listToClass(['info-bar', visible && 'visible'])}>
58
67
  <div className="space"></div>
59
68
  <div className="content">
60
- <IconButton aria-label={t.removeConfig} title={t.removeConfig} onClick={removeAll}><Times /></IconButton>
69
+ {shouldRenderRemoveAllButton && (
70
+ <IconButton aria-label={t.removeConfig} title={t.removeConfig} onClick={removeAll}><Times /></IconButton>
71
+ )}
61
72
  <FadingOverflow className="list-overflow" scroll="arrows" enableHorizontalScrollWithVerticalWheel>
62
73
  <ul>
63
74
  {currentSelection && (
@@ -67,7 +78,12 @@ export const InfoBar = () => {
67
78
  )}
68
79
  {currentStack && (
69
80
  <li>
70
- <InfoBadge label={currentStack.label} dismiss={t.removeStack} color="cyan" onDismiss={() => chat.set('stack', undefined)} />
81
+ <InfoBadge
82
+ label={currentStack.label}
83
+ dismiss={t.removeStack}
84
+ color="cyan"
85
+ onDismiss={features.stack ? (() => chat.set('stack', undefined)) : undefined}
86
+ />
71
87
  </li>
72
88
  )}
73
89
  {currentWorkspace && (
@@ -76,7 +92,7 @@ export const InfoBar = () => {
76
92
  label={currentWorkspace.label}
77
93
  dismiss={t.removeWorkspace}
78
94
  color="pink"
79
- onDismiss={() => chat.set('workspace', undefined)}
95
+ onDismiss={features.workspace ? (() => chat.set('workspace', undefined)) : undefined}
80
96
  />
81
97
  </li>
82
98
  )}
@@ -139,7 +139,8 @@ export const QuickCommandSelector = ({ inputRef }: Props) => {
139
139
  const filter = useMemo(() => value === '/' || quickCommandRegex.test(value) ? value.substring(1) : undefined, [value])
140
140
  const [isClosed, setClosed] = useState(false)
141
141
  const selectorRef = useRef<HTMLDivElement>(null)
142
- const shouldRender = filter !== undefined && !isClosed
142
+ const isEnabled = useCurrentChatState('features').quickCommands
143
+ const shouldRender = isEnabled && filter !== undefined && !isClosed
143
144
 
144
145
  // Resets the closed state whenever the message input is cleared
145
146
  useEffect(() => {
@@ -3,7 +3,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { AdaptiveTextArea } from '../../components/AdaptiveTextArea'
4
4
  import { ProgressBar } from '../../components/ProgressBar'
5
5
  import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../context/hooks'
6
- import { MessageInputFeatures } from '../../features'
7
6
  import { quickCommandRegex } from '../../regex'
8
7
  import { ChatEntry } from '../../state/ChatEntry'
9
8
  import { ButtonGroup } from './ButtonGroup'
@@ -12,19 +11,12 @@ import { InfoBar } from './InfoBar'
12
11
  import { QuickCommandSelector } from './QuickCommandSelector'
13
12
  import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled'
14
13
 
15
- interface Props {
16
- /**
17
- * The features enabled and accessible through the message input.
18
- */
19
- features: MessageInputFeatures,
20
- }
21
-
22
14
  /**
23
15
  * This renders the MessageInput part of the layout which includes the progress bar, the actual textarea, the badges telling what is
24
16
  * going to be used for the question and the buttons to send, cancel, set the workspace, among others. This also includes the Quick
25
17
  * Commands panel for auto completing.
26
18
  */
27
- export const MessageInput = ({ features }: Props) => {
19
+ export const MessageInput = () => {
28
20
  const t = useMessageInputDictionary()
29
21
  const [focused, setFocused] = useState(false)
30
22
  const [expanded, setExpanded] = useState(true)
@@ -77,7 +69,6 @@ export const MessageInput = ({ features }: Props) => {
77
69
  maxHeight={isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT}
78
70
  />
79
71
  <ButtonGroup
80
- features={features}
81
72
  onSend={onSend}
82
73
  onCancel={() => chat.abort()}
83
74
  expanded={expanded}
@@ -13,6 +13,7 @@ export const MessageInputBox = styled.div`
13
13
 
14
14
  > .info-bar {
15
15
  margin-top: 8px;
16
+ margin-bottom: -3px;
16
17
  position: relative;
17
18
  overflow: hidden;
18
19
 
@@ -35,15 +36,18 @@ export const MessageInputBox = styled.div`
35
36
  border-top-left-radius: 10px;
36
37
  border-top-right-radius: 10px;
37
38
  height: ${INFO_BAR_HEIGHT}px;
38
- padding: 0 4px;
39
+ padding: 6px 4px 0;
39
40
  background-color: ${theme.color.light[500]};
40
41
  display: flex;
41
42
  flex-direction: row;
42
- align-items: center;
43
43
  gap: 6px;
44
44
 
45
45
  .list-overflow {
46
46
  max-width: calc(100% - 30px); // close button + gap
47
+ height: 24px;
48
+ &:first-child {
49
+ margin-left: 0.25rem; // space added to the left when the close all button isn't rendered
50
+ }
47
51
  }
48
52
 
49
53
  ul {
@@ -6,7 +6,7 @@ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
6
6
  import { useCallback, useRef, useState } from 'react'
7
7
  import { styled } from 'styled-components'
8
8
  import { FadingOverflow } from '../components/FadingOverflow'
9
- import { useChatState, useChatTabs } from '../context/hooks'
9
+ import { useCurrentChatState } from '../context/hooks'
10
10
  import { MinimizedActions } from '../types'
11
11
 
12
12
  const Header = styled.header`
@@ -53,8 +53,7 @@ const Header = styled.header`
53
53
  */
54
54
  export const MinimizedHeader = ({ onClose, onCollapse, onExpand }: MinimizedActions) => {
55
55
  const t = useTranslate(dictionary)
56
- const { active } = useChatTabs()
57
- const label = useChatState(active, 'label')
56
+ const label = useCurrentChatState('label')
58
57
  const [collapsed, setCollapsed] = useState(false)
59
58
  const ref = useRef<HTMLElement>(null)
60
59
  const showButtons = onClose || onCollapse || onExpand
@@ -75,10 +75,11 @@ const StacksTab = ({ visibility }: { visibility: VisibilityLevelEnum }) => {
75
75
  }
76
76
  className="option-list"
77
77
  />}
78
- {!!stacks.length && !filtered.length && <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
78
+ {!!stacks.length && !filtered.length &&
79
+ <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder"/>}
79
80
  {!stacks.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
80
81
  </div>
81
- <Button onClick={submit} disabled={!value}>{t.apply}</Button>
82
+ {!!filtered.length && <Button onClick={submit} disabled={!value}>{t.apply}</Button>}
82
83
  </>
83
84
  )
84
85
  }
@@ -65,10 +65,11 @@ const WorkspacesPanel = () => {
65
65
  }
66
66
  className="option-list"
67
67
  />}
68
- {!!workspaces.length && !filtered.length && <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
68
+ {!!workspaces.length && !filtered.length &&
69
+ <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder" />}
69
70
  {!workspaces.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
70
71
  </div>
71
- <Button onClick={submit} disabled={!value}>{t.apply}</Button>
72
+ {!!filtered.length && <Button onClick={submit} disabled={!value}>{t.apply}</Button>}
72
73
  </>
73
74
  )
74
75
  }
@@ -1,2 +0,0 @@
1
- export declare const Agents: () => null;
2
- //# sourceMappingURL=Agents.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Agents.d.ts","sourceRoot":"","sources":["../../src/views/Agents.tsx"],"names":[],"mappings":"AAoEA,eAAO,MAAM,MAAM,YAclB,CAAA"}