@stack-spot/ai-chat-widget 1.22.0 → 1.23.1

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 (78) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/StackspotAIWidget.d.ts +5 -1
  3. package/dist/StackspotAIWidget.d.ts.map +1 -1
  4. package/dist/StackspotAIWidget.js +2 -2
  5. package/dist/StackspotAIWidget.js.map +1 -1
  6. package/dist/app-metadata.json +2 -2
  7. package/dist/components/AgentCard/AgentCardCreate.d.ts +4 -0
  8. package/dist/components/AgentCard/AgentCardCreate.d.ts.map +1 -0
  9. package/dist/components/AgentCard/AgentCardCreate.js +34 -0
  10. package/dist/components/AgentCard/AgentCardCreate.js.map +1 -0
  11. package/dist/components/AgentCard/dictionary.d.ts +2 -0
  12. package/dist/components/AgentCard/dictionary.d.ts.map +1 -1
  13. package/dist/components/AgentCard/dictionary.js +2 -0
  14. package/dist/components/AgentCard/dictionary.js.map +1 -1
  15. package/dist/components/Code.d.ts.map +1 -1
  16. package/dist/components/Code.js +44 -15
  17. package/dist/components/Code.js.map +1 -1
  18. package/dist/components/QuickStartButton.d.ts +1 -7
  19. package/dist/components/QuickStartButton.d.ts.map +1 -1
  20. package/dist/components/QuickStartButton.js +4 -44
  21. package/dist/components/QuickStartButton.js.map +1 -1
  22. package/dist/components/TabManager.d.ts.map +1 -1
  23. package/dist/components/TabManager.js +5 -2
  24. package/dist/components/TabManager.js.map +1 -1
  25. package/dist/layout.css +17 -1
  26. package/dist/types.d.ts +2 -1
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/views/ChatTabSelection.d.ts.map +1 -1
  29. package/dist/views/ChatTabSelection.js +5 -2
  30. package/dist/views/ChatTabSelection.js.map +1 -1
  31. package/dist/views/Home/BuiltInAgent.d.ts.map +1 -1
  32. package/dist/views/Home/BuiltInAgent.js +5 -6
  33. package/dist/views/Home/BuiltInAgent.js.map +1 -1
  34. package/dist/views/Home/CustomAgent.d.ts.map +1 -1
  35. package/dist/views/Home/CustomAgent.js +1 -2
  36. package/dist/views/Home/CustomAgent.js.map +1 -1
  37. package/dist/views/Home/index.d.ts +1 -1
  38. package/dist/views/Home/index.d.ts.map +1 -1
  39. package/dist/views/Home/index.js +3 -8
  40. package/dist/views/Home/index.js.map +1 -1
  41. package/dist/views/Home/styled.d.ts.map +1 -1
  42. package/dist/views/Home/styled.js +26 -12
  43. package/dist/views/Home/styled.js.map +1 -1
  44. package/dist/views/Home/types.d.ts +4 -0
  45. package/dist/views/Home/types.d.ts.map +1 -1
  46. package/dist/views/MessageInput/ButtonBar.d.ts +1 -5
  47. package/dist/views/MessageInput/ButtonBar.d.ts.map +1 -1
  48. package/dist/views/MessageInput/ButtonBar.js +2 -2
  49. package/dist/views/MessageInput/ButtonBar.js.map +1 -1
  50. package/dist/views/MessageInput/SelectContent.d.ts.map +1 -1
  51. package/dist/views/MessageInput/SelectContent.js +2 -2
  52. package/dist/views/MessageInput/SelectContent.js.map +1 -1
  53. package/dist/views/MessageInput/index.js +1 -1
  54. package/dist/views/MessageInput/index.js.map +1 -1
  55. package/dist/views/MessageInput/styled.d.ts +1 -3
  56. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  57. package/dist/views/MessageInput/styled.js +4 -5
  58. package/dist/views/MessageInput/styled.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/StackspotAIWidget.tsx +6 -1
  61. package/src/app-metadata.json +2 -2
  62. package/src/components/AgentCard/AgentCardCreate.tsx +45 -0
  63. package/src/components/AgentCard/dictionary.ts +2 -0
  64. package/src/components/Code.tsx +91 -51
  65. package/src/components/QuickStartButton.tsx +9 -57
  66. package/src/components/TabManager.tsx +28 -12
  67. package/src/layout.css +17 -1
  68. package/src/types.ts +2 -1
  69. package/src/views/ChatTabSelection.tsx +5 -2
  70. package/src/views/Home/BuiltInAgent.tsx +7 -15
  71. package/src/views/Home/CustomAgent.tsx +0 -3
  72. package/src/views/Home/index.tsx +3 -7
  73. package/src/views/Home/styled.ts +26 -12
  74. package/src/views/Home/types.ts +4 -0
  75. package/src/views/MessageInput/ButtonBar.tsx +2 -6
  76. package/src/views/MessageInput/SelectContent.tsx +3 -2
  77. package/src/views/MessageInput/index.tsx +2 -2
  78. package/src/views/MessageInput/styled.ts +5 -6
@@ -2,7 +2,8 @@
2
2
  * Copied from the extension's webview.
3
3
  */
4
4
 
5
- import { AddCode, ChevronDoubleDown, Collapse, Copy } from '@citric/icons'
5
+ import { Text } from '@citric/core'
6
+ import { AddCode, ChevronDoubleDown, Collapse, Copy, Download } from '@citric/icons'
6
7
  import { IconButton } from '@citric/ui'
7
8
  import { theme, useThemeKind } from '@stack-spot/portal-theme'
8
9
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
@@ -12,6 +13,7 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
12
13
  import { materialDark, vs } from 'react-syntax-highlighter/dist/esm/styles/prism'
13
14
  import styled from 'styled-components'
14
15
  import { WithChildren } from '../types'
16
+ import { languages } from '../utils/programming-languages'
15
17
 
16
18
  export type CodeAction = (
17
19
  code: string,
@@ -53,23 +55,28 @@ const CodeBox = styled.code`
53
55
  }
54
56
  }
55
57
 
56
- .action-bar {
58
+ .header-code {
57
59
  display: flex;
58
60
  flex-direction: row;
59
- justify-content: flex-end;
61
+ justify-content: space-between;
60
62
  background: ${theme.color.light[600]};
61
63
  padding: 4px 6px;
62
64
  border-top-left-radius: 6px;
63
65
  border-top-right-radius: 6px;
64
66
 
65
- button {
66
- background: transparent;
67
- border: none;
68
- opacity: 0.75;
69
- transition: opacity 0.2s;
67
+ .action-bar {
68
+ display: flex;
69
+ flex-direction: row;
70
+
71
+ button {
72
+ background: transparent;
73
+ border: none;
74
+ opacity: 0.75;
75
+ transition: opacity 0.2s;
70
76
 
71
- &:hover {
72
- opacity: 1;
77
+ &:hover {
78
+ opacity: 1;
79
+ }
73
80
  }
74
81
  }
75
82
  }
@@ -108,6 +115,7 @@ export const Code = ({
108
115
  const match = /language-(\w+)/.exec(className || '')
109
116
  const computedLanguage = language ?? (match ?? [])[1]?.toLowerCase() ?? 'txt'
110
117
  const content = String(children ?? '').replaceAll(/\n\t/g, '\n').trim()
118
+ const languageExtension = languages.find(l => l.value === computedLanguage)?.extensions?.[0] ?? '.txt'
111
119
 
112
120
  function onClickInsert() {
113
121
  onInsertCode?.(content, computedLanguage)
@@ -137,47 +145,75 @@ export const Code = ({
137
145
  return (
138
146
  <CodeBox className={['code-box', themeKind, className].join(' ')}>
139
147
  {showActionBar && (
140
- <div className="action-bar" role="toolbar">
141
- <IconButton
142
- aria-label={showLines ? t.hideLines : t.showLines}
143
- title={showLines ? t.hideLines : t.showLines}
144
- onClick={() => setShowLines(v => !v)}
145
- style={{ position: 'relative', transform: showLines ? undefined : 'rotate(180deg)', transition: 'transform 0.2s' }}
146
- >
147
- <Collapse />
148
- </IconButton>
149
- <IconButton
150
- aria-label={t.copy}
151
- title={t.copy}
152
- onClick={onClickCopy}
153
- style={{ position: 'relative' }}
154
- >
155
- <Copy />
156
- </IconButton>
157
- {onInsertCode
158
- ? (
159
- <IconButton
160
- aria-label={t.insert}
161
- title={t.insert}
162
- onClick={onClickInsert}
163
- style={{ position: 'relative' }}
164
- >
165
- <ChevronDoubleDown style={{ transform: 'rotate(90deg)' }} />
166
- </IconButton>
167
- )
168
- : null}
169
- {onNewFile
170
- ? (
171
- <IconButton
172
- aria-label={t.newFile}
173
- title={t.newFile}
174
- onClick={onClickNewFile}
175
- style={{ position: 'relative' }}
176
- >
177
- <AddCode />
178
- </IconButton>
179
- )
180
- : null}
148
+ <div className="header-code">
149
+ <div>
150
+ <Text appearance="code2">{computedLanguage ?? ''}</Text>
151
+ </div>
152
+ <div className="action-bar" role="toolbar">
153
+ <IconButton
154
+ aria-label={showLines ? t.hideLines : t.showLines}
155
+ title={showLines ? t.hideLines : t.showLines}
156
+ onClick={() => setShowLines(v => !v)}
157
+ style={{ position: 'relative', transform: showLines ? undefined : 'rotate(180deg)', transition: 'transform 0.2s' }}
158
+ >
159
+ <Collapse />
160
+ </IconButton>
161
+ <IconButton
162
+ aria-label={t.downloadCode}
163
+ title={t.downloadCode}
164
+ onClick={() => {
165
+ try {
166
+ const extension = languageExtension
167
+ const filename = `download${extension}`
168
+ const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
169
+ const link = document.createElement('a')
170
+ link.href = URL.createObjectURL(blob)
171
+ link.download = filename
172
+ document.body.appendChild(link)
173
+ link.click()
174
+ document.body.removeChild(link)
175
+ URL.revokeObjectURL(link.href)
176
+ } catch (e) {
177
+ // eslint-disable-next-line no-console
178
+ console.error(t.downloadError, e)
179
+ }
180
+ }}
181
+ >
182
+ <Download />
183
+ </IconButton>
184
+ <IconButton
185
+ aria-label={t.copy}
186
+ title={t.copy}
187
+ onClick={onClickCopy}
188
+ style={{ position: 'relative' }}
189
+ >
190
+ <Copy />
191
+ </IconButton>
192
+ {onInsertCode
193
+ ? (
194
+ <IconButton
195
+ aria-label={t.insert}
196
+ title={t.insert}
197
+ onClick={onClickInsert}
198
+ style={{ position: 'relative' }}
199
+ >
200
+ <ChevronDoubleDown style={{ transform: 'rotate(90deg)' }} />
201
+ </IconButton>
202
+ )
203
+ : null}
204
+ {onNewFile
205
+ ? (
206
+ <IconButton
207
+ aria-label={t.newFile}
208
+ title={t.newFile}
209
+ onClick={onClickNewFile}
210
+ style={{ position: 'relative' }}
211
+ >
212
+ <AddCode />
213
+ </IconButton>
214
+ )
215
+ : null}
216
+ </div>
181
217
  </div>
182
218
  )}
183
219
  <div>
@@ -205,6 +241,8 @@ const dictionary = {
205
241
  newFile: 'Creates a new file with this code as its content',
206
242
  hideLines: 'Hide line numbers',
207
243
  showLines: 'Show line numbers',
244
+ downloadCode: 'Download the code',
245
+ downloadError: 'Error downloading code',
208
246
  },
209
247
  pt: {
210
248
  copy: 'Copiar código para a área de transferência',
@@ -212,5 +250,7 @@ const dictionary = {
212
250
  newFile: 'Criar um novo arquivo com este código como conteúdo',
213
251
  hideLines: 'Esconder números das linhas',
214
252
  showLines: 'Mostrar números das linhas',
253
+ downloadCode: 'Fazer download do código',
254
+ downloadError: 'Erro ao fazer download do código',
215
255
  },
216
256
  } satisfies Dictionary
@@ -1,66 +1,18 @@
1
- import { IconBox, Text } from '@citric/core'
2
- import { theme, WithStyle } from '@stack-spot/portal-theme'
3
- import { styled } from 'styled-components'
1
+ import { Button, IconBox, Text } from '@citric/core'
2
+ import { WithStyle } from '@stack-spot/portal-theme'
4
3
  import { ButtonAction } from '../types'
5
- import { FadingOverflow } from './FadingOverflow'
6
4
 
7
- interface Props extends ButtonAction, WithStyle {
8
- background?: string,
9
- /**
10
- * Whether or not the vertical overflow of this button should be managed by the component {@link FadingOverflow}.
11
- * @default false
12
- */
13
- manageOverflow?: boolean,
14
- }
15
-
16
- /**
17
- * A button for quick starting a conversation.
18
- */
19
- const QuickButton = styled.button<{ $color?: string, $bg?: string }>`
20
- display: flex;
21
- flex-direction: column;
22
- padding: 12px;
23
- gap: 12px;
24
- background-color: ${theme.color.light[500]};
25
- border: none;
26
- color: inherit;
27
- flex: 1;
28
- border-radius: 4px;
29
- border: 2px solid;
30
- border-color: ${theme.color.light[500]};
31
- transition: border-color 0.3s;
32
- cursor: pointer;
33
- text-align: left;
34
-
35
- &:hover {
36
- border-color: ${theme.color.light[600]};
37
- }
38
-
39
- i {
40
- border-radius: 2px;
41
- display: flex;
42
- align-items: center;
43
- justify-content: center;
44
- background-color: ${({ $bg }) => $bg || 'transparent'};
45
- ${({ $color }) => $color
46
- ? `
47
- svg {
48
- fill: ${$color};
49
- }`
50
- : ''
51
- };
52
- }
53
- `
5
+ interface Props extends ButtonAction, WithStyle { }
54
6
 
55
- export const QuickStartButton = ({ label, onClick, background, className, color, icon, style, manageOverflow }: Props) => {
7
+ export const QuickStartButton = ({ label, onClick, className, icon, style }: Props) => {
56
8
  const content = <>
57
- {icon && <IconBox aria-hidden>{icon}</IconBox>}
58
- <Text>{label}</Text>
9
+ {icon && <IconBox color="light" aria-hidden>{icon}</IconBox>}
10
+ <Text nowrapEllipsis>{label}</Text>
59
11
  </>
60
12
 
61
13
  return (
62
- <QuickButton className={className} style={style} onClick={onClick} $color={color} $bg={background}>
63
- {manageOverflow ? <FadingOverflow sides={['top', 'bottom']} scroll="wheel">{content}</FadingOverflow> : content}
64
- </QuickButton>
14
+ <Button colorScheme="light" size="md" className={className} style={style} onClick={onClick}>
15
+ {content}
16
+ </Button>
65
17
  )
66
18
  }
@@ -1,5 +1,6 @@
1
1
  /* Tabs to select chat and add new chats */
2
2
 
3
+ import { Button, IconBox } from '@citric/core'
3
4
  import { TimesMini } from '@citric/icons'
4
5
  import { IconButton } from '@citric/ui'
5
6
  import { listToClass, theme } from '@stack-spot/portal-theme'
@@ -188,18 +189,33 @@ export function TabManager<T, Key extends React.Key>(
188
189
  </li>
189
190
  )), [tabs, active])
190
191
 
191
- const extras = useMemo(() => buttons.map(({ label, onClick, icon, className, style }) => (
192
- <IconButton
193
- key={label}
194
- aria-label={label}
195
- title={label}
196
- className={listToClass([className, 'extra'])}
197
- style={style}
198
- onClick={onClick}
199
- >
200
- {icon}
201
- </IconButton>
202
- )), [buttons])
192
+ const extras = useMemo(() => buttons.map(({ ariaLabel, label, onClick, icon, className, style }) => (
193
+ <>
194
+ {label ?
195
+ <Button
196
+ sx={{ paddingLeft: 0, marginRight: '4px', fontWeight: '400' }}
197
+ colorScheme="light"
198
+ size="md"
199
+ key={ariaLabel}
200
+ title={label}
201
+ style={style}
202
+ onClick={onClick}>
203
+ <IconBox size="sm" sx={{ width: '32px', height: '16px', paddingInline: '8px' }} >
204
+ {icon}
205
+ </IconBox>
206
+ {label}
207
+ </Button> :
208
+ <IconButton
209
+ key={ariaLabel}
210
+ aria-label={ariaLabel}
211
+ title={label}
212
+ className={listToClass([className, 'extra'])}
213
+ style={style}
214
+ onClick={onClick}
215
+ >
216
+ {icon}
217
+ </IconButton>}
218
+ </>)), [buttons])
203
219
 
204
220
  // when a new tab is added, we should scroll to it if there are more tabs that we can show.
205
221
  useEffect(() => {
package/src/layout.css CHANGED
@@ -52,7 +52,6 @@
52
52
  .home-page {
53
53
  .title {
54
54
  margin-top: 40px;
55
- font-size: 18px;
56
55
  }
57
56
  .subtitle {
58
57
  font-size: 14px;
@@ -100,6 +99,23 @@
100
99
  display: flex;
101
100
  flex-direction: column;
102
101
  flex: 1;
102
+ .agent-list .agent-card {
103
+ flex-direction: row;
104
+ align-items: center;
105
+ gap: 8px;
106
+ border-radius: 4px;
107
+ padding: 10px 8px;
108
+ img {
109
+ width: 32px;
110
+ height: 32px;
111
+ border-radius: 4px;
112
+ }
113
+ h6 {
114
+ font-weight: 400;
115
+ font-size: 13px;
116
+ line-height: 14px;
117
+ }
118
+ }
103
119
  }
104
120
 
105
121
  .chat-content {
package/src/types.ts CHANGED
@@ -9,7 +9,8 @@ export type PropsOf<T extends React.FunctionComponent> = T extends React.Functio
9
9
  export interface ButtonAction extends WithStyle {
10
10
  icon?: React.ReactElement,
11
11
  color?: string,
12
- label: string,
12
+ label?: string,
13
+ ariaLabel?: string,
13
14
  onClick: () => any,
14
15
  }
15
16
 
@@ -41,13 +41,14 @@ export const ChatTabSelection = () => {
41
41
  () => {
42
42
  const actions: ButtonAction[] = [{
43
43
  icon: <Plus />,
44
- label: t.newChat,
44
+ ariaLabel: t.newChat,
45
45
  onClick: create,
46
46
  }]
47
47
  if (chatHistory) {
48
48
  actions.push({
49
49
  icon: <Clock />,
50
- label: t.openHistory,
50
+ label: t.history,
51
+ ariaLabel: t.openHistory,
51
52
  className: 'test',
52
53
  style: { marginLeft: 'auto' },
53
54
  onClick: () => widget.set('panel', 'history'),
@@ -71,10 +72,12 @@ export const ChatTabSelection = () => {
71
72
 
72
73
  const dictionary = {
73
74
  en: {
75
+ history: 'History',
74
76
  openHistory: 'Open chat history',
75
77
  newChat: 'New chat',
76
78
  },
77
79
  pt: {
80
+ history: 'Histórico',
78
81
  openHistory: 'Abrir histórico da conversa',
79
82
  newChat: 'Novo chat',
80
83
  },
@@ -1,6 +1,5 @@
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'
1
+ import { Text } from '@citric/core'
2
+ import { FaceSmile, KnowledgeSource, QuickCommand, StackSpot } from '@citric/icons'
4
3
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
5
4
  import { QuickStartButton } from '../../components/QuickStartButton'
6
5
  import { useCurrentChat } from '../../context/hooks'
@@ -21,35 +20,28 @@ export const BuiltInAgent = ({ username }: HomeProps) => {
21
20
 
22
21
  return (
23
22
  <HomeBox className="home-page">
24
- <h2 className="title">{t.hello}, {username}</h2>
25
- <h3 className="subtitle">{t.subtitle}</h3>
23
+ <Text appearance="h2" className="title">{t.hello}, {username}</Text>
24
+ <Text appearance="h3" className="subtitle">{t.subtitle}</Text>
26
25
  <div className="shortcuts">
27
26
  <QuickStartButton
28
27
  label={t['question.meta']}
29
28
  onClick={() => send(t['question.meta'])}
30
- icon={<MiniLogo />}
31
- background={theme.color.orange[50]}
29
+ icon={<StackSpot />}
32
30
  />
33
31
  <QuickStartButton
34
32
  label={t['question.ks']}
35
33
  onClick={() => send(t['question.ks'])}
36
34
  icon={<KnowledgeSource />}
37
- background={theme.color.cyan[50]}
38
- color={theme.color.cyan[600]}
39
35
  />
40
36
  <QuickStartButton
41
37
  label={t['question.agents']}
42
38
  onClick={() => send(t['question.agents'])}
43
39
  icon={<FaceSmile />}
44
- background={theme.color.pink[50]}
45
- color={theme.color.pink[600]}
46
40
  />
47
41
  <QuickStartButton
48
42
  label={t['question.qc']}
49
43
  onClick={() => send(t['question.qc'])}
50
44
  icon={<QuickCommand />}
51
- background={theme.color.purple[50]}
52
- color={theme.color.purple[600]}
53
45
  />
54
46
  </div>
55
47
  </HomeBox>
@@ -59,7 +51,7 @@ export const BuiltInAgent = ({ username }: HomeProps) => {
59
51
  const dictionary = {
60
52
  en: {
61
53
  hello: 'Hello',
62
- subtitle: "Let's innovate and streamline your coding journey together. Want to lead the change? Just ask!",
54
+ subtitle: 'What really matters takes time, let us help you with the rest.',
63
55
  'question.meta': 'What is StackSpot AI?',
64
56
  'question.ks': 'What are Knowledge Sources?',
65
57
  'question.agents': 'How do Agents work?',
@@ -67,7 +59,7 @@ const dictionary = {
67
59
  },
68
60
  pt: {
69
61
  hello: 'Olá',
70
- subtitle: 'Vamos inovar e simplificar sua jornada de programação juntos. Quer liderar a mudança? É perguntar!',
62
+ subtitle: 'O que realmente importa leva tempo, deixe que a gente cuide do resto.',
71
63
  'question.meta': 'O que é a StackSpot AI?',
72
64
  'question.ks': 'O que são Knowledge Sources?',
73
65
  'question.agents': 'Como funcionam os agentes?',
@@ -1,7 +1,6 @@
1
1
  import { IconBox, Text } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
3
  import { agentClient } from '@stack-spot/portal-network'
4
- import { theme } from '@stack-spot/portal-theme'
5
4
  import { useMemo } from 'react'
6
5
  import { QuickStartButton } from '../../components/QuickStartButton'
7
6
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
@@ -20,8 +19,6 @@ export const CustomAgent = () => {
20
19
  key={index}
21
20
  label={prompt}
22
21
  onClick={() => send(prompt)}
23
- background={theme.color.light[500]}
24
- manageOverflow
25
22
  />
26
23
  )), [agent?.suggested_prompts])
27
24
 
@@ -1,6 +1,7 @@
1
1
  import { FadingOverflow } from '@stack-spot/portal-components/FadingOverflow'
2
2
  import { styled } from 'styled-components'
3
3
  import { AgentCard } from '../../components/AgentCard'
4
+ import { AgentCardCreate } from '../../components/AgentCard/AgentCardCreate'
4
5
  import { FallbackBoundary } from '../../components/FallbackBoundary'
5
6
  import { useCurrentChatState } from '../../context/hooks'
6
7
  import { BuiltInAgent } from './BuiltInAgent'
@@ -17,12 +18,6 @@ const AgentList = styled.ul`
17
18
 
18
19
  li {
19
20
  flex-shrink: 0;
20
- &:first-child {
21
- margin-left: auto;
22
- }
23
- &:last-child {
24
- margin-right: auto;
25
- }
26
21
  }
27
22
  `
28
23
 
@@ -31,7 +26,7 @@ const AgentList = styled.ul`
31
26
  *
32
27
  * The home page can be replaced by providing children to the component `StackspotAIWidget`.
33
28
  */
34
- export const Home = ({ initialAgents, ...props }: HomeProps) => {
29
+ export const Home = ({ initialAgents, urlCreateAgent, ...props }: HomeProps) => {
35
30
  const agent = useCurrentChatState('agent')
36
31
  const isAgentEnabled = useCurrentChatState('features').agent
37
32
 
@@ -41,6 +36,7 @@ export const Home = ({ initialAgents, ...props }: HomeProps) => {
41
36
  {!!initialAgents && isAgentEnabled && (
42
37
  <FadingOverflow enableHorizontalScrollWithVerticalWheel sides={['left', 'right']} scroll="arrows">
43
38
  <AgentList className="agent-list">
39
+ {urlCreateAgent && <li><AgentCardCreate urlCreateAgent={urlCreateAgent} /></li>}
44
40
  {initialAgents.map((agent) => <li key={agent.id}><AgentCard agent={agent} /></li>)}
45
41
  </AgentList>
46
42
  </FadingOverflow>
@@ -1,12 +1,10 @@
1
+ import { theme } from '@stack-spot/portal-theme'
1
2
  import { styled } from 'styled-components'
2
3
 
3
4
  export const HomeBox = styled.div`
4
5
  margin: auto 0;
5
6
 
6
7
  .title, .subtitle {
7
- font-family: 'San Francisco';
8
- font-size: 26px;
9
- font-weight: 600;
10
8
  margin: 0;
11
9
  }
12
10
 
@@ -14,22 +12,36 @@ export const HomeBox = styled.div`
14
12
  display: inline-block;
15
13
  background: linear-gradient(72.81deg, #FF9900 0.96%, #FF6633 100%);
16
14
  background-clip: text;
17
- margin-bottom: 10px;
18
15
  color: transparent;
19
16
  }
20
17
 
21
18
  .subtitle {
22
19
  color: #A0A0A0;
23
- margin-bottom: 20px;
20
+ margin-bottom: 24px;
24
21
  }
25
22
 
26
23
  .shortcuts {
27
- display: flex;
28
- flex-direction: row;
29
- gap: 15px;
30
24
  li {
31
25
  flex: 1;
32
26
  }
27
+ button {
28
+ padding-left: 0;
29
+ border-radius: 50px;
30
+ padding-inline: 16px;
31
+ padding-block: 8px;
32
+ font-weight: 400;
33
+ line-height: normal;
34
+ border: none;
35
+ max-width: 30vw;
36
+ i {
37
+ width: 16px;
38
+ height: 16px;
39
+ margin-right: 8px;
40
+ }
41
+ &:hover, &:visited, &:active, &:focus {
42
+ background-color: ${theme.color.light[600]}
43
+ }
44
+ }
33
45
  }
34
46
 
35
47
  .avatar, .avatar svg {
@@ -45,14 +57,16 @@ export const HomeBox = styled.div`
45
57
  gap: 20px;
46
58
 
47
59
  .shortcuts {
48
- margin-top: 10px;
60
+ display: inline-block;
61
+ text-align: center;
49
62
  button {
50
- padding: 16px;
51
- height: 100px;
52
- line-height: 24px;
63
+ margin-bottom: 10px;
64
+ margin-left: 4px;
65
+ margin-right: 4px;
53
66
  p {
54
67
  overflow: hidden;
55
68
  }
69
+
56
70
  }
57
71
  }
58
72
  }
@@ -9,4 +9,8 @@ export interface HomeProps {
9
9
  * Initial agents to show in home.
10
10
  */
11
11
  initialAgents?: LabeledAgent[],
12
+ /**
13
+ * URL to redirect to the "Create Agent" page on the portal
14
+ */
15
+ urlCreateAgent?: string,
12
16
  }
@@ -15,20 +15,16 @@ interface SelectionBarProps {
15
15
  * Whether or not the message is currently being sent. This is used to decide which button to show: send or cancel.
16
16
  */
17
17
  isLoading?: boolean,
18
- /**
19
- * State to determine whether the input is focused
20
- */
21
- focused?: boolean,
22
18
  }
23
19
 
24
- export const ButtonBar = ({ onSend, isLoading, focused }: SelectionBarProps) => {
20
+ export const ButtonBar = ({ onSend, isLoading }: SelectionBarProps) => {
25
21
  const t = useMessageInputDictionary()
26
22
  const chat = useCurrentChat()
27
23
  const widget = useWidget()
28
24
  const features = useCurrentChatState('features')
29
25
 
30
26
  return (
31
- <SelectionBarWrapper className="button-group" $inputFocused={focused}>
27
+ <SelectionBarWrapper className="button-group">
32
28
  <Flex sx={{ gap: '4px' }}>
33
29
  <SelectContent />
34
30
  {features.editor && (
@@ -1,4 +1,4 @@
1
- import { Clip, KnowledgeSource, Spaces, Stack } from '@citric/icons'
1
+ import { KnowledgeSource, Plus, Spaces, Stack } from '@citric/icons'
2
2
  import { IconButton } from '@citric/ui'
3
3
  import { SelectionList } from '@stack-spot/portal-components/SelectionList'
4
4
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
@@ -52,6 +52,7 @@ export const SelectContent = () => {
52
52
  return (
53
53
  <>
54
54
  <IconButton
55
+ id="button-content-select"
55
56
  color="light"
56
57
  appearance="square"
57
58
  role="button"
@@ -59,7 +60,7 @@ export const SelectContent = () => {
59
60
  data-test-hint="button-options"
60
61
  aria-label={visibleMenu ? t.collapse : t.expand}
61
62
  onClick={() => setVisibleMenu(state => !state)}>
62
- <Clip />
63
+ <Plus />
63
64
  </IconButton>
64
65
  <SelectionList
65
66
  style={{
@@ -81,9 +81,9 @@ export const MessageInput = () => {
81
81
  </div>
82
82
  </div>
83
83
  <ProgressBar visible={true} animate={isLoading}
84
- backgroundColor={isLoading || !focused ? theme.color.light[600] : theme.color.primary[500]} />
84
+ backgroundColor={isLoading || !focused ? theme.color.light[500] : theme.color.primary[500]} />
85
85
  <InfoBar />
86
- <ButtonBar focused={focused} onSend={onSend} isLoading={isLoading} />
86
+ <ButtonBar onSend={onSend} isLoading={isLoading} />
87
87
  </MessageInputBox>
88
88
  )
89
89
  }