@stack-spot/ai-chat-widget 1.2.0 → 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 (178) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/StackspotAIWidget.d.ts +1 -6
  3. package/dist/StackspotAIWidget.d.ts.map +1 -1
  4. package/dist/StackspotAIWidget.js +6 -11
  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 +1 -1
  24. package/dist/context/hooks.d.ts.map +1 -1
  25. package/dist/context/hooks.js +4 -5
  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 +22 -10
  45. package/dist/state/WidgetState.d.ts.map +1 -1
  46. package/dist/state/WidgetState.js +24 -9
  47. package/dist/state/WidgetState.js.map +1 -1
  48. package/dist/views/Agents/AgentDescription.d.ts +9 -0
  49. package/dist/views/Agents/AgentDescription.d.ts.map +1 -0
  50. package/dist/views/Agents/AgentDescription.js +21 -0
  51. package/dist/views/Agents/AgentDescription.js.map +1 -0
  52. package/dist/views/Agents/AgentsPanel.d.ts +5 -0
  53. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -0
  54. package/dist/views/Agents/AgentsPanel.js +19 -0
  55. package/dist/views/Agents/AgentsPanel.js.map +1 -0
  56. package/dist/views/Agents/AgentsTab.d.ts +5 -0
  57. package/dist/views/Agents/AgentsTab.d.ts.map +1 -0
  58. package/dist/views/Agents/AgentsTab.js +43 -0
  59. package/dist/views/Agents/AgentsTab.js.map +1 -0
  60. package/dist/views/Agents/dictionary.d.ts +2 -0
  61. package/dist/views/Agents/dictionary.d.ts.map +1 -0
  62. package/dist/views/Agents/dictionary.js +35 -0
  63. package/dist/views/Agents/dictionary.js.map +1 -0
  64. package/dist/views/Agents/index.d.ts +5 -0
  65. package/dist/views/Agents/index.d.ts.map +1 -0
  66. package/dist/views/Agents/index.js +21 -0
  67. package/dist/views/Agents/index.js.map +1 -0
  68. package/dist/views/Agents/styled.d.ts +3 -0
  69. package/dist/views/Agents/styled.d.ts.map +1 -0
  70. package/dist/views/Agents/styled.js +58 -0
  71. package/dist/views/Agents/styled.js.map +1 -0
  72. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  73. package/dist/views/Chat/ChatMessage.js +1 -1
  74. package/dist/views/Chat/ChatMessage.js.map +1 -1
  75. package/dist/views/Chat/index.js +1 -1
  76. package/dist/views/Chat/index.js.map +1 -1
  77. package/dist/views/Chat/styled.js +1 -1
  78. package/dist/views/ChatTabSelection.d.ts +1 -5
  79. package/dist/views/ChatTabSelection.d.ts.map +1 -1
  80. package/dist/views/ChatTabSelection.js +6 -5
  81. package/dist/views/ChatTabSelection.js.map +1 -1
  82. package/dist/views/Editor.d.ts.map +1 -1
  83. package/dist/views/Editor.js +4 -1
  84. package/dist/views/Editor.js.map +1 -1
  85. package/dist/views/Home/BuiltInAgent.d.ts +6 -0
  86. package/dist/views/Home/BuiltInAgent.d.ts.map +1 -0
  87. package/dist/views/{Home.js → Home/BuiltInAgent.js} +7 -41
  88. package/dist/views/Home/BuiltInAgent.js.map +1 -0
  89. package/dist/views/Home/CustomAgent.d.ts +5 -0
  90. package/dist/views/Home/CustomAgent.d.ts.map +1 -0
  91. package/dist/views/Home/CustomAgent.js +24 -0
  92. package/dist/views/Home/CustomAgent.js.map +1 -0
  93. package/dist/views/Home/index.d.ts +8 -0
  94. package/dist/views/Home/index.d.ts.map +1 -0
  95. package/dist/views/Home/index.js +15 -0
  96. package/dist/views/Home/index.js.map +1 -0
  97. package/dist/views/Home/styled.d.ts +2 -0
  98. package/dist/views/Home/styled.d.ts.map +1 -0
  99. package/dist/views/Home/styled.js +59 -0
  100. package/dist/views/Home/styled.js.map +1 -0
  101. package/dist/views/Home/types.d.ts +7 -0
  102. package/dist/views/Home/types.d.ts.map +1 -0
  103. package/dist/views/Home/types.js +2 -0
  104. package/dist/views/Home/types.js.map +1 -0
  105. package/dist/views/KnowledgeSources.js +1 -1
  106. package/dist/views/KnowledgeSources.js.map +1 -1
  107. package/dist/views/MessageInput/ButtonGroup.d.ts +1 -6
  108. package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
  109. package/dist/views/MessageInput/ButtonGroup.js +12 -4
  110. package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
  111. package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
  112. package/dist/views/MessageInput/InfoBar.js +16 -6
  113. package/dist/views/MessageInput/InfoBar.js.map +1 -1
  114. package/dist/views/MessageInput/QuickCommandSelector.js +3 -3
  115. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
  116. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  117. package/dist/views/MessageInput/index.d.ts +1 -9
  118. package/dist/views/MessageInput/index.d.ts.map +1 -1
  119. package/dist/views/MessageInput/index.js +2 -2
  120. package/dist/views/MessageInput/index.js.map +1 -1
  121. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  122. package/dist/views/MessageInput/styled.js +6 -2
  123. package/dist/views/MessageInput/styled.js.map +1 -1
  124. package/dist/views/MinimizedHeader.d.ts.map +1 -1
  125. package/dist/views/MinimizedHeader.js +2 -3
  126. package/dist/views/MinimizedHeader.js.map +1 -1
  127. package/dist/views/Stacks.js +2 -1
  128. package/dist/views/Stacks.js.map +1 -1
  129. package/dist/views/Workspaces.js +2 -1
  130. package/dist/views/Workspaces.js.map +1 -1
  131. package/package.json +2 -2
  132. package/src/StackspotAIWidget.tsx +6 -16
  133. package/src/chat-interceptors/quick-commands.ts +1 -1
  134. package/src/chat-interceptors/send-message.ts +22 -2
  135. package/src/components/AutoFocus.tsx +9 -1
  136. package/src/components/FadingOverflow.tsx +2 -2
  137. package/src/components/QuickStartButton.tsx +17 -5
  138. package/src/components/RightPanelForm.tsx +2 -1
  139. package/src/context/hooks.ts +7 -8
  140. package/src/features.ts +27 -24
  141. package/src/index.ts +6 -0
  142. package/src/layout.css +7 -0
  143. package/src/state/ChatState.ts +26 -4
  144. package/src/state/ChatTabsController.ts +50 -11
  145. package/src/state/WidgetState.ts +39 -13
  146. package/src/views/Agents/AgentDescription.tsx +48 -0
  147. package/src/views/Agents/AgentsPanel.tsx +19 -0
  148. package/src/views/Agents/AgentsTab.tsx +80 -0
  149. package/src/views/Agents/dictionary.ts +36 -0
  150. package/src/views/Agents/index.tsx +26 -0
  151. package/src/views/Agents/styled.ts +59 -0
  152. package/src/views/Chat/ChatMessage.tsx +19 -17
  153. package/src/views/Chat/index.tsx +1 -1
  154. package/src/views/Chat/styled.ts +1 -1
  155. package/src/views/ChatTabSelection.tsx +7 -9
  156. package/src/views/Editor.tsx +4 -1
  157. package/src/views/{Home.tsx → Home/BuiltInAgent.tsx} +7 -48
  158. package/src/views/Home/CustomAgent.tsx +39 -0
  159. package/src/views/Home/index.tsx +20 -0
  160. package/src/views/Home/styled.ts +59 -0
  161. package/src/views/Home/types.ts +6 -0
  162. package/src/views/KnowledgeSources.tsx +2 -2
  163. package/src/views/MessageInput/ButtonGroup.tsx +15 -12
  164. package/src/views/MessageInput/InfoBar.tsx +25 -9
  165. package/src/views/MessageInput/QuickCommandSelector.tsx +3 -3
  166. package/src/views/MessageInput/index.tsx +1 -10
  167. package/src/views/MessageInput/styled.ts +6 -2
  168. package/src/views/MinimizedHeader.tsx +2 -3
  169. package/src/views/Stacks.tsx +3 -2
  170. package/src/views/Workspaces.tsx +3 -2
  171. package/dist/views/Agents.d.ts +0 -2
  172. package/dist/views/Agents.d.ts.map +0 -1
  173. package/dist/views/Agents.js +0 -146
  174. package/dist/views/Agents.js.map +0 -1
  175. package/dist/views/Home.d.ts +0 -14
  176. package/dist/views/Home.d.ts.map +0 -1
  177. package/dist/views/Home.js.map +0 -1
  178. package/src/views/Agents.tsx +0 -203
@@ -0,0 +1,80 @@
1
+ import { Button, Text } from '@citric/core'
2
+ import { Search } from '@citric/icons'
3
+ import { Placeholder } from '@stack-spot/portal-components/Placeholder'
4
+ import { MiniLogo } from '@stack-spot/portal-components/svg'
5
+ import { agentClient } from '@stack-spot/portal-network'
6
+ import { AgentResponse, VisibilityLevel } from '@stack-spot/portal-network/api/agent'
7
+ import { useMemo, useState } from 'react'
8
+ import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
9
+ import { IconInput } from '../../components/IconInput'
10
+ import { useCurrentChat } from '../../context/hooks'
11
+ import { useRightPanel } from '../../right-panel/hooks'
12
+ import { AgentDescription } from './AgentDescription'
13
+ import { useAgentsDictionary } from './dictionary'
14
+ import { AgentLabel } from './styled'
15
+
16
+ export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' }) => {
17
+ const t = useAgentsDictionary()
18
+ const { close } = useRightPanel()
19
+ const chat = useCurrentChat()
20
+ const [filter, setFilter] = useState('')
21
+ const defaultAgent = useMemo(() => ({
22
+ id: '',
23
+ name: 'Stackspot AI',
24
+ description: t.defaultAgentDescription,
25
+ llm_config: { model_slug: 'gpt4o' },
26
+ } as AgentResponse), [])
27
+ const agents = visibility === 'BUILT-IN' ? agentClient.publicAgents.useQuery({}) : agentClient.agents.useQuery({ visibility })
28
+ const [value, setValue] = useState<AgentResponse | undefined>(
29
+ chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : defaultAgent,
30
+ )
31
+ const filtered = useMemo(
32
+ () => {
33
+ const ags = visibility === 'BUILT-IN' ? [defaultAgent as AgentResponse, ...agents] : agents
34
+ return filter ? ags.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : ags
35
+ },
36
+ [agents, filter, value],
37
+ )
38
+
39
+ function submit() {
40
+ if (value) {
41
+ chat.set('agent', value.id ? { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN' } : undefined)
42
+ }
43
+ close()
44
+ }
45
+
46
+ return (
47
+ <>
48
+ <div className="content">
49
+ <IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
50
+ {!!filtered.length && <DescribedRadioGroup
51
+ options={filtered}
52
+ keygen={a => a.id}
53
+ value={value}
54
+ onChange={setValue}
55
+ renderLabel={({ name, avatar, id }) => (
56
+ <AgentLabel>
57
+ {id ? (avatar && <img src={avatar} />) : <MiniLogo />}
58
+ <Text>{name}</Text>
59
+ </AgentLabel>
60
+ )}
61
+ renderDescription={a => <AgentDescription
62
+ agentId={a.id}
63
+ description={a.description}
64
+ llm={a.llm_config?.model_slug}
65
+ numberOfKnowledgeSources={a.knowledge_sources_config?.knowledge_sources?.length ?? 0}
66
+ />}
67
+ optionClassName={a => (a === value && filter && !a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
68
+ ? 'filtered-out'
69
+ : ''
70
+ }
71
+ className="option-list"
72
+ />}
73
+ {!!agents.length && !filtered.length &&
74
+ <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder" />}
75
+ {!agents.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
76
+ </div>
77
+ {!!filtered.length && <Button onClick={submit} disabled={!value}>{t.apply}</Button>}
78
+ </>
79
+ )
80
+ }
@@ -0,0 +1,36 @@
1
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
2
+
3
+ const dictionary = {
4
+ en: {
5
+ title: 'Agents',
6
+ subtitle: 'By selecting an Agent, it will be consulted to generate the answers.',
7
+ personal: 'Personal',
8
+ builtin: 'Built-in',
9
+ shared: 'Shared',
10
+ account: 'Account',
11
+ apply: 'Apply',
12
+ noSearchResults: "Your search didn't yield results.",
13
+ noSearchResultsDescription: 'Please, try another search term.',
14
+ noData: 'There are no agents in this category yet.',
15
+ noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
16
+ defaultAgentDescription: 'The StackSpot CodeGen is an advanced artificial intelligence agent designed to optimize and accelerate software development. Integrated directly into your integrated development environment, StackSpot CodeGen offers real-time code suggestions, helping developers write high-quality code more efficiently. With robust features such as creating Stacks AI, customized knowledge sources, and quick commands, StackSpot CodeGen contextualizes your development needs to provide the best answers and code suggestions.',
17
+ description: 'Description',
18
+ },
19
+ pt: {
20
+ title: 'Agentes',
21
+ subtitle: 'Ao selecionar um Agente, ele será consultado para gerar as respostas.',
22
+ personal: 'Pessoal',
23
+ builtin: 'Embutido',
24
+ shared: 'Compartilhado',
25
+ account: 'Conta',
26
+ apply: 'Aplicar',
27
+ noSearchResults: 'Sua busca não produziu resultados.',
28
+ noSearchResultsDescription: 'Por favor, tente outra busca.',
29
+ noData: 'Ainda não há agentes nesta categoria.',
30
+ noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
31
+ defaultAgentDescription: 'O StackSpot CodeGen é um agente de inteligência artificial avançado projetado para otimizar e acelerar o desenvolvimento de software. Integrado diretamente ao seu ambiente de desenvolvimento, o StackSpot CodeGen oferece sugestões de código em tempo real, ajudando os desenvolvedores a escreverem código de alta qualidade de forma mais eficiente. Com recursos robustos, como a criação de Stacks AI, Knowledge Sources personalizadas e comandos rápidos, o StackSpot CodeGen contextualiza suas necessidades de desenvolvimento para fornecer as melhores respostas e sugestões de código.',
32
+ description: 'Descrição',
33
+ },
34
+ } satisfies Dictionary
35
+
36
+ export const useAgentsDictionary = () => useTranslate(dictionary)
@@ -0,0 +1,26 @@
1
+ import { useEffect } from 'react'
2
+ import { useWidget, useWidgetState } from '../../context/hooks'
3
+ import { useRightPanel } from '../../right-panel/hooks'
4
+ import { AgentsPanel } from './AgentsPanel'
5
+ import { useAgentsDictionary } from './dictionary'
6
+
7
+ /**
8
+ * Renders the panel to select an agent of this panel is opened.
9
+ */
10
+ export const Agents = () => {
11
+ const t = useAgentsDictionary()
12
+ const panel = useWidgetState('panel')
13
+ const { open } = useRightPanel()
14
+ const widget = useWidget()
15
+
16
+ useEffect(() => {
17
+ if (panel === 'agent') open(
18
+ <AgentsPanel />,
19
+ { title: t.title, description: t.subtitle, onClose: () => widget.set('panel', undefined) },
20
+ )
21
+ }, [panel, t])
22
+
23
+ return null
24
+ }
25
+
26
+
@@ -0,0 +1,59 @@
1
+ import { theme } from '@stack-spot/portal-theme'
2
+ import { styled } from 'styled-components'
3
+
4
+ export const AgentLabel = styled.div`
5
+ display: flex;
6
+ flex-direction: row;
7
+ align-items: center;
8
+ gap: 6px;
9
+
10
+ img, svg {
11
+ width: 20px;
12
+ height: 20px;
13
+ border-radius: 50%;
14
+ overflow: hidden;
15
+ }
16
+ `
17
+
18
+ export const AgentDescriptionBox = styled.div`
19
+ color: ${theme.color.light[700]};
20
+ line-height: 18px;
21
+
22
+ section {
23
+ border-bottom: 1px solid ${theme.color.light[600]};
24
+ padding-bottom: 10px;
25
+ margin-bottom: 10px;
26
+
27
+ &:last-child {
28
+ padding-bottom: 0;
29
+ margin-bottom: 0;
30
+ border-bottom: none;
31
+ }
32
+
33
+ .title {
34
+ display: block;
35
+ margin-bottom: 6px;
36
+ }
37
+ }
38
+
39
+ ul {
40
+ padding: 0;
41
+ margin: 0;
42
+ list-style: none;
43
+ display: flex;
44
+ flex-direction: row;
45
+ flex-wrap: wrap;
46
+ white-space: nowrap;
47
+ gap: 6px;
48
+
49
+ li {
50
+ margin: 3px 0;
51
+ }
52
+ }
53
+
54
+ .ks-skeleton {
55
+ width: 100px;
56
+ height: 12px;
57
+ mix-blend-mode: color-dodge;
58
+ }
59
+ `
@@ -86,21 +86,23 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
86
86
  ? <Markdown onCopyCode={(code) => onCopyCode(code, entry.messageId ?? '', chat)}>{entry.content}</Markdown>
87
87
  : <p className="plain-text">{entry.content}</p>
88
88
  }
89
- {entry.actions?.length && <div className="actions">
90
- {entry.actions.map(
91
- (a, index) => (
92
- <Button
93
- key={index}
94
- appearance={a.appearance === 'primary' ? 'contained' : 'outlined'}
95
- colorScheme="inverse"
96
- onClick={() => runAction(a)}
97
- disabled={!isLast}
98
- >
99
- {a.title}
100
- </Button>
101
- ),
102
- )}
103
- </div>}
89
+ {entry.actions?.length && (
90
+ <div className="actions">
91
+ {entry.actions.map(
92
+ (a, index) => (
93
+ <Button
94
+ key={index}
95
+ appearance={a.appearance === 'primary' ? 'contained' : 'outlined'}
96
+ colorScheme="inverse"
97
+ onClick={() => runAction(a)}
98
+ disabled={!isLast}
99
+ >
100
+ {a.title}
101
+ </Button>
102
+ ),
103
+ )}
104
+ </div>
105
+ )}
104
106
  </div>}
105
107
  </div>
106
108
  {entry.error && (
@@ -111,8 +113,8 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
111
113
  )}
112
114
  {!!entry.knowledgeSources?.length && <div className="ks-box">
113
115
  <Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
114
- <ul>{entry.knowledgeSources.map(ks => (
115
- <li key={ks.slug}>
116
+ <ul>{entry.knowledgeSources.map((ks, index) => (
117
+ <li key={index}>
116
118
  <Button size="sm" colorScheme="light" onClick={() => detailKS(ks)}>{ks.name}</Button>
117
119
  </li>
118
120
  ))}</ul>
@@ -13,5 +13,5 @@ interface Props {
13
13
  */
14
14
  export const Chat = ({ username }: Props) => {
15
15
  const { active } = useChatTabs()
16
- return <ChatMessages key={active} chatId={active} username={username} />
16
+ return <ChatMessages key={active.id} chatId={active.id} username={username} />
17
17
  }
@@ -132,12 +132,12 @@ export const ChatList = styled.ul`
132
132
 
133
133
  &.user {
134
134
  align-items: end;
135
+ margin-left: 15%;
135
136
 
136
137
  .chat-message {
137
138
  display: flex;
138
139
  flex-direction: row;
139
140
  gap: 8px;
140
- align-items: center;
141
141
 
142
142
  .message-content {
143
143
  padding: 10px;
@@ -2,13 +2,9 @@ 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'
5
+ import { useChatState, useChatTabs, useWidget, useWidgetState } from '../context/hooks'
6
6
  import { ButtonAction } from '../types'
7
7
 
8
- interface Props {
9
- history?: boolean,
10
- }
11
-
12
8
  const TabLabel = ({ id }: { id: string }) => {
13
9
  const label = useChatState(id, 'label')
14
10
  return <div title={label}>{label}</div>
@@ -17,9 +13,10 @@ const TabLabel = ({ id }: { id: string }) => {
17
13
  /**
18
14
  * This renders the top-most part of the layout, which includes the chat selection through tabs.
19
15
  */
20
- export const ChatTabSelection = ({ history }: Props) => {
16
+ export const ChatTabSelection = () => {
21
17
  const t = useTranslate(dictionary)
22
18
  const widget = useWidget()
19
+ const { chatHistory } = useWidgetState('features') ?? {}
23
20
  const { active, chats } = useChatTabs()
24
21
 
25
22
  const buttons = useMemo<ButtonAction[]>(
@@ -29,7 +26,7 @@ export const ChatTabSelection = ({ history }: Props) => {
29
26
  label: t.newChat,
30
27
  onClick: () => widget.createChat(),
31
28
  }]
32
- if (history) {
29
+ if (chatHistory) {
33
30
  actions.push({
34
31
  icon: <Clock />,
35
32
  label: t.openHistory,
@@ -40,11 +37,12 @@ export const ChatTabSelection = ({ history }: Props) => {
40
37
  }
41
38
  return actions
42
39
  },
43
- [history],
40
+ [chatHistory],
44
41
  )
42
+
45
43
  return <TabManager
46
44
  tabs={chats}
47
- active={active}
45
+ active={active.id}
48
46
  renderLabel={({ id }) => <TabLabel id={id} />}
49
47
  keygen={({ id }) => id}
50
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">