@stack-spot/ai-chat-widget 1.2.0 → 1.3.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.
- package/CHANGELOG.md +16 -0
- package/dist/StackspotAIWidget.d.ts +1 -6
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +6 -11
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +1 -1
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +21 -2
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/AutoFocus.d.ts.map +1 -1
- package/dist/components/AutoFocus.js +8 -1
- package/dist/components/AutoFocus.js.map +1 -1
- package/dist/components/FadingOverflow.js +2 -2
- package/dist/components/FadingOverflow.js.map +1 -1
- package/dist/components/QuickStartButton.d.ts +6 -1
- package/dist/components/QuickStartButton.d.ts.map +1 -1
- package/dist/components/QuickStartButton.js +6 -2
- package/dist/components/QuickStartButton.js.map +1 -1
- package/dist/components/RightPanelForm.d.ts.map +1 -1
- package/dist/components/RightPanelForm.js +2 -1
- package/dist/components/RightPanelForm.js.map +1 -1
- package/dist/context/hooks.d.ts +1 -1
- package/dist/context/hooks.d.ts.map +1 -1
- package/dist/context/hooks.js +4 -5
- package/dist/context/hooks.js.map +1 -1
- package/dist/features.d.ts +16 -17
- package/dist/features.d.ts.map +1 -1
- package/dist/features.js +17 -9
- package/dist/features.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout.css +7 -0
- package/dist/state/ChatState.d.ts +23 -3
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js +5 -2
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/ChatTabsController.d.ts +21 -3
- package/dist/state/ChatTabsController.d.ts.map +1 -1
- package/dist/state/ChatTabsController.js +49 -11
- package/dist/state/ChatTabsController.js.map +1 -1
- package/dist/state/WidgetState.d.ts +22 -10
- package/dist/state/WidgetState.d.ts.map +1 -1
- package/dist/state/WidgetState.js +24 -9
- package/dist/state/WidgetState.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts +9 -0
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -0
- package/dist/views/Agents/AgentDescription.js +21 -0
- package/dist/views/Agents/AgentDescription.js.map +1 -0
- package/dist/views/Agents/AgentsPanel.d.ts +5 -0
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -0
- package/dist/views/Agents/AgentsPanel.js +19 -0
- package/dist/views/Agents/AgentsPanel.js.map +1 -0
- package/dist/views/Agents/AgentsTab.d.ts +5 -0
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -0
- package/dist/views/Agents/AgentsTab.js +43 -0
- package/dist/views/Agents/AgentsTab.js.map +1 -0
- package/dist/views/Agents/dictionary.d.ts +2 -0
- package/dist/views/Agents/dictionary.d.ts.map +1 -0
- package/dist/views/Agents/dictionary.js +35 -0
- package/dist/views/Agents/dictionary.js.map +1 -0
- package/dist/views/Agents/index.d.ts +5 -0
- package/dist/views/Agents/index.d.ts.map +1 -0
- package/dist/views/Agents/index.js +21 -0
- package/dist/views/Agents/index.js.map +1 -0
- package/dist/views/Agents/styled.d.ts +3 -0
- package/dist/views/Agents/styled.d.ts.map +1 -0
- package/dist/views/Agents/styled.js +58 -0
- package/dist/views/Agents/styled.js.map +1 -0
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +1 -1
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/index.js +1 -1
- package/dist/views/Chat/index.js.map +1 -1
- package/dist/views/Chat/styled.js +1 -1
- package/dist/views/ChatTabSelection.d.ts +1 -5
- package/dist/views/ChatTabSelection.d.ts.map +1 -1
- package/dist/views/ChatTabSelection.js +6 -5
- package/dist/views/ChatTabSelection.js.map +1 -1
- package/dist/views/Editor.d.ts.map +1 -1
- package/dist/views/Editor.js +4 -1
- package/dist/views/Editor.js.map +1 -1
- package/dist/views/Home/BuiltInAgent.d.ts +6 -0
- package/dist/views/Home/BuiltInAgent.d.ts.map +1 -0
- package/dist/views/{Home.js → Home/BuiltInAgent.js} +7 -41
- package/dist/views/Home/BuiltInAgent.js.map +1 -0
- package/dist/views/Home/CustomAgent.d.ts +5 -0
- package/dist/views/Home/CustomAgent.d.ts.map +1 -0
- package/dist/views/Home/CustomAgent.js +24 -0
- package/dist/views/Home/CustomAgent.js.map +1 -0
- package/dist/views/Home/index.d.ts +8 -0
- package/dist/views/Home/index.d.ts.map +1 -0
- package/dist/views/Home/index.js +15 -0
- package/dist/views/Home/index.js.map +1 -0
- package/dist/views/Home/styled.d.ts +2 -0
- package/dist/views/Home/styled.d.ts.map +1 -0
- package/dist/views/Home/styled.js +59 -0
- package/dist/views/Home/styled.js.map +1 -0
- package/dist/views/Home/types.d.ts +7 -0
- package/dist/views/Home/types.d.ts.map +1 -0
- package/dist/views/Home/types.js +2 -0
- package/dist/views/Home/types.js.map +1 -0
- package/dist/views/KnowledgeSources.js +1 -1
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts +1 -6
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.js +12 -4
- package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
- package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
- package/dist/views/MessageInput/InfoBar.js +16 -6
- package/dist/views/MessageInput/InfoBar.js.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +3 -3
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/index.d.ts +1 -9
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +2 -2
- package/dist/views/MessageInput/index.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +6 -2
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/MinimizedHeader.d.ts.map +1 -1
- package/dist/views/MinimizedHeader.js +2 -3
- package/dist/views/MinimizedHeader.js.map +1 -1
- package/dist/views/Stacks.js +2 -1
- package/dist/views/Stacks.js.map +1 -1
- package/dist/views/Workspaces.js +2 -1
- package/dist/views/Workspaces.js.map +1 -1
- package/package.json +3 -4
- package/src/StackspotAIWidget.tsx +6 -16
- package/src/chat-interceptors/quick-commands.ts +1 -1
- package/src/chat-interceptors/send-message.ts +22 -2
- package/src/components/AutoFocus.tsx +9 -1
- package/src/components/FadingOverflow.tsx +2 -2
- package/src/components/QuickStartButton.tsx +17 -5
- package/src/components/RightPanelForm.tsx +2 -1
- package/src/context/hooks.ts +7 -8
- package/src/features.ts +27 -24
- package/src/index.ts +6 -0
- package/src/layout.css +7 -0
- package/src/state/ChatState.ts +26 -4
- package/src/state/ChatTabsController.ts +50 -11
- package/src/state/WidgetState.ts +39 -13
- package/src/views/Agents/AgentDescription.tsx +48 -0
- package/src/views/Agents/AgentsPanel.tsx +19 -0
- package/src/views/Agents/AgentsTab.tsx +80 -0
- package/src/views/Agents/dictionary.ts +36 -0
- package/src/views/Agents/index.tsx +26 -0
- package/src/views/Agents/styled.ts +59 -0
- package/src/views/Chat/ChatMessage.tsx +19 -17
- package/src/views/Chat/index.tsx +1 -1
- package/src/views/Chat/styled.ts +1 -1
- package/src/views/ChatTabSelection.tsx +7 -9
- package/src/views/Editor.tsx +4 -1
- package/src/views/{Home.tsx → Home/BuiltInAgent.tsx} +7 -48
- package/src/views/Home/CustomAgent.tsx +39 -0
- package/src/views/Home/index.tsx +20 -0
- package/src/views/Home/styled.ts +59 -0
- package/src/views/Home/types.ts +6 -0
- package/src/views/KnowledgeSources.tsx +2 -2
- package/src/views/MessageInput/ButtonGroup.tsx +15 -12
- package/src/views/MessageInput/InfoBar.tsx +25 -9
- package/src/views/MessageInput/QuickCommandSelector.tsx +3 -3
- package/src/views/MessageInput/index.tsx +1 -10
- package/src/views/MessageInput/styled.ts +6 -2
- package/src/views/MinimizedHeader.tsx +2 -3
- package/src/views/Stacks.tsx +3 -2
- package/src/views/Workspaces.tsx +3 -2
- package/dist/views/Agents.d.ts +0 -2
- package/dist/views/Agents.d.ts.map +0 -1
- package/dist/views/Agents.js +0 -146
- package/dist/views/Agents.js.map +0 -1
- package/dist/views/Home.d.ts +0 -14
- package/dist/views/Home.d.ts.map +0 -1
- package/dist/views/Home.js.map +0 -1
- 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 &&
|
|
90
|
-
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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={
|
|
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>
|
package/src/views/Chat/index.tsx
CHANGED
|
@@ -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
|
}
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -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 = (
|
|
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 (
|
|
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
|
-
[
|
|
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)}
|
package/src/views/Editor.tsx
CHANGED
|
@@ -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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
-
*
|
|
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
|
|
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
|
+
`
|
|
@@ -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 = ({
|
|
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
|
|
42
|
+
const features = useCurrentChatState('features')
|
|
43
|
+
const hasFeatureButtons = features.agent || features.workspace || features.knowledgeSource || features.stack || features.editor
|
|
48
44
|
|
|
49
|
-
useEffect(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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">
|