@stack-spot/ai-chat-widget 1.14.0-beta.7 → 1.14.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.
- package/CHANGELOG.md +15 -0
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/app-metadata.json +19 -19
- package/dist/components/Accordion.d.ts.map +1 -1
- package/dist/components/Accordion.js +3 -1
- package/dist/components/Accordion.js.map +1 -1
- package/dist/components/ButtonFavorite.d.ts +47 -0
- package/dist/components/ButtonFavorite.d.ts.map +1 -0
- package/dist/components/ButtonFavorite.js +25 -0
- package/dist/components/ButtonFavorite.js.map +1 -0
- package/dist/components/Code.d.ts.map +1 -1
- package/dist/components/FadingOverflow.d.ts.map +1 -1
- package/dist/components/FallbackBoundary/index.d.ts.map +1 -1
- package/dist/components/IconInput.d.ts.map +1 -1
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Modal.d.ts.map +1 -1
- package/dist/components/ProgressBar.d.ts.map +1 -1
- package/dist/components/QuickStartButton.d.ts.map +1 -1
- package/dist/components/RightPanelForm.d.ts.map +1 -1
- package/dist/components/RightPanelTabs.d.ts.map +1 -1
- package/dist/components/Selector/index.d.ts +9 -1
- package/dist/components/Selector/index.d.ts.map +1 -1
- package/dist/components/Selector/index.js +19 -13
- package/dist/components/Selector/index.js.map +1 -1
- package/dist/components/StackedBadge.d.ts.map +1 -1
- package/dist/components/ToolBadge.d.ts.map +1 -1
- package/dist/components/Tooltip/context.d.ts.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.d.ts +1 -1
- package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.js +5 -5
- package/dist/components/form/DescribedCheckboxGroup.js.map +1 -1
- package/dist/components/form/DescribedRadioGroup.d.ts +1 -1
- package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedRadioGroup.js +4 -4
- package/dist/components/form/DescribedRadioGroup.js.map +1 -1
- package/dist/components/form/dictionary.d.ts +19 -0
- package/dist/components/form/dictionary.d.ts.map +1 -0
- package/dist/components/form/dictionary.js +19 -0
- package/dist/components/form/dictionary.js.map +1 -0
- package/dist/components/form/styled.d.ts.map +1 -1
- package/dist/components/form/styled.js +1 -2
- package/dist/components/form/styled.js.map +1 -1
- package/dist/components/form/types.d.ts +8 -0
- package/dist/components/form/types.d.ts.map +1 -1
- package/dist/right-panel/DefaultPanel.d.ts.map +1 -1
- package/dist/right-panel/RightPanelProvider.d.ts.map +1 -1
- package/dist/utils/agent.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
- package/dist/views/Agents/AgentsPanel.js +2 -0
- package/dist/views/Agents/AgentsPanel.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +7 -2
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Agents/dictionary.d.ts +1 -1
- package/dist/views/Agents/dictionary.d.ts.map +1 -1
- package/dist/views/Agents/dictionary.js +2 -0
- package/dist/views/Agents/dictionary.js.map +1 -1
- package/dist/views/Agents/useAgentFavorites.d.ts +8 -0
- package/dist/views/Agents/useAgentFavorites.d.ts.map +1 -0
- package/dist/views/Agents/useAgentFavorites.js +31 -0
- package/dist/views/Agents/useAgentFavorites.js.map +1 -0
- package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +2 -2
- package/dist/views/Chat/ChatMessages.d.ts.map +1 -1
- package/dist/views/Chat/StepsList.d.ts.map +1 -1
- package/dist/views/Chat/index.d.ts.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +3 -2
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/Home/BuiltInAgent.d.ts.map +1 -1
- package/dist/views/Home/index.d.ts.map +1 -1
- package/dist/views/KnowledgeSources.d.ts.map +1 -1
- package/dist/views/KnowledgeSources.js +35 -4
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/AgentSelector.js +6 -4
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +25 -2
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MinimizedHeader.d.ts.map +1 -1
- package/dist/views/Stacks.d.ts.map +1 -1
- package/dist/views/Stacks.js +33 -3
- package/dist/views/Stacks.js.map +1 -1
- package/dist/views/Steps/FlowChart/HandleGroup.d.ts.map +1 -1
- package/dist/views/Steps/FlowChart/NodeStep.d.ts.map +1 -1
- package/dist/views/Steps/FlowChart/index.d.ts.map +1 -1
- package/dist/views/Steps/StepModal.d.ts.map +1 -1
- package/dist/views/Steps/StepsPanel.d.ts.map +1 -1
- package/dist/views/Workspaces.d.ts.map +1 -1
- package/dist/views/Workspaces.js +42 -5
- package/dist/views/Workspaces.js.map +1 -1
- package/package.json +3 -3
- package/src/app-metadata.json +19 -19
- package/src/components/Accordion.tsx +3 -2
- package/src/components/ButtonFavorite.tsx +100 -0
- package/src/components/Selector/index.tsx +45 -16
- package/src/components/form/DescribedCheckboxGroup.tsx +27 -11
- package/src/components/form/DescribedRadioGroup.tsx +16 -1
- package/src/components/form/dictionary.ts +18 -0
- package/src/components/form/styled.ts +1 -2
- package/src/components/form/types.ts +11 -1
- package/src/views/Agents/AgentsPanel.tsx +2 -0
- package/src/views/Agents/AgentsTab.tsx +12 -2
- package/src/views/Agents/dictionary.ts +2 -0
- package/src/views/Agents/useAgentFavorites.ts +32 -0
- package/src/views/Chat/ChatMessage.tsx +2 -2
- package/src/views/ChatHistory/utils.ts +3 -2
- package/src/views/KnowledgeSources.tsx +45 -8
- package/src/views/MessageInput/AgentSelector.tsx +8 -3
- package/src/views/MessageInput/QuickCommandSelector.tsx +25 -1
- package/src/views/Stacks.tsx +39 -4
- package/src/views/Workspaces.tsx +49 -8
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Button, IconBox } from '@citric/core'
|
|
2
|
+
import { Star, StarFill } from '@citric/icons'
|
|
3
|
+
import { IconButton, LoadingCircular } from '@citric/ui'
|
|
4
|
+
import { useTranslate } from '@stack-spot/portal-translate'
|
|
5
|
+
import { useEffect, useState } from 'react'
|
|
6
|
+
import { dictionaryFavorites } from './form/dictionary'
|
|
7
|
+
|
|
8
|
+
interface ItemFavorite {
|
|
9
|
+
id?: string,
|
|
10
|
+
slug?: string,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Favorite<T extends ItemFavorite> {
|
|
14
|
+
/**
|
|
15
|
+
* id or slug to be used on functions
|
|
16
|
+
*/
|
|
17
|
+
idOrSlug: string,
|
|
18
|
+
/**
|
|
19
|
+
* Array containing the list of favorite items.
|
|
20
|
+
*/
|
|
21
|
+
listFavorites?: T[],
|
|
22
|
+
/**
|
|
23
|
+
* Function to add an item to the list of favorites.
|
|
24
|
+
* @param idOrSlug - The ID or slug of the content to be added to the favorites list.
|
|
25
|
+
* @returns void
|
|
26
|
+
*/
|
|
27
|
+
onAddFavorite?: (idOrSlug: string) => void,
|
|
28
|
+
/**
|
|
29
|
+
* Function to remove an item from the list of favorites.
|
|
30
|
+
* @param idOrSlug - The ID or slug of the content to be removed from the favorites list.
|
|
31
|
+
* @returns void
|
|
32
|
+
*/
|
|
33
|
+
onRemoveFavorite?: (idOrSlug: string) => void,
|
|
34
|
+
/**
|
|
35
|
+
* Indicates whether a request to add a favorite item is pending.
|
|
36
|
+
*/
|
|
37
|
+
pendingAddFav?: boolean,
|
|
38
|
+
/**
|
|
39
|
+
* Indicates whether a request to remove a favorite item is pending.
|
|
40
|
+
*/
|
|
41
|
+
pendingRemoveFav?: boolean,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface Props<T extends ItemFavorite> {
|
|
45
|
+
/**
|
|
46
|
+
* Determine whether the component should be a Button or an IconButton.
|
|
47
|
+
*/
|
|
48
|
+
isIconButton?: boolean,
|
|
49
|
+
/**
|
|
50
|
+
* An object containing favorite items and related operations.
|
|
51
|
+
*/
|
|
52
|
+
favorite: Favorite<T>,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const ButtonFavorite = <T extends ItemFavorite>({ favorite, isIconButton = false }: Props<T>) => {
|
|
56
|
+
const [isFavorite, setIsFavorite] = useState(false)
|
|
57
|
+
const { idOrSlug, listFavorites, pendingAddFav, pendingRemoveFav } = favorite || {}
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
setIsFavorite(!!listFavorites?.some((fav) => fav.slug === idOrSlug || fav.id === idOrSlug))
|
|
61
|
+
}, [listFavorites])
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<ButtonWrapper isFavorite={isFavorite} isIconButton={isIconButton} favorite={favorite}>
|
|
65
|
+
<IconBox colorIcon={isFavorite ? 'yellow.500' : 'light'}>
|
|
66
|
+
{pendingAddFav || pendingRemoveFav ?
|
|
67
|
+
<LoadingCircular colorScheme="warning"/> : isFavorite ? <StarFill/> : <Star/> }
|
|
68
|
+
</IconBox>
|
|
69
|
+
</ButtonWrapper>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ButtonWrapper = <T extends ItemFavorite>({ children, isIconButton, isFavorite, favorite }:
|
|
74
|
+
{children: React.ReactNode, isIconButton?: boolean, isFavorite: boolean, favorite: Favorite<T>}) => {
|
|
75
|
+
const t = useTranslate(dictionaryFavorites)
|
|
76
|
+
const { idOrSlug, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav } = favorite
|
|
77
|
+
if (isIconButton){
|
|
78
|
+
return (
|
|
79
|
+
<IconButton
|
|
80
|
+
as="button"
|
|
81
|
+
disabled={pendingAddFav || pendingRemoveFav}
|
|
82
|
+
aria-label={`${t[isFavorite ? 'remove' : 'become']} ${t.favorite}`}
|
|
83
|
+
title={`${t[isFavorite ? 'remove' : 'become']} ${t.favorite}`}
|
|
84
|
+
onClick={() => isFavorite ? onRemoveFavorite?.(idOrSlug || '') : onAddFavorite?.(idOrSlug || '')}>
|
|
85
|
+
{children}
|
|
86
|
+
</IconButton>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
return (
|
|
90
|
+
<Button
|
|
91
|
+
as="button"
|
|
92
|
+
colorScheme="light"
|
|
93
|
+
disabled={pendingAddFav || pendingRemoveFav}
|
|
94
|
+
aria-label={`${t[isFavorite ? 'remove' : 'become']} ${t.favorite}`}
|
|
95
|
+
title={`${t[isFavorite ? 'remove' : 'become']} ${t.favorite}`}
|
|
96
|
+
onClick={() => isFavorite ? onRemoveFavorite?.(idOrSlug || '') : onAddFavorite?.(idOrSlug || '')} >
|
|
97
|
+
{children}
|
|
98
|
+
</Button>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -7,6 +7,7 @@ import { Dictionary, interpolate, useTranslate } from '@stack-spot/portal-transl
|
|
|
7
7
|
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
8
8
|
import { useCurrentChatState } from '../../context/hooks'
|
|
9
9
|
import { getUrlToStackSpotAI } from '../../utils/url'
|
|
10
|
+
import { ButtonFavorite } from '../ButtonFavorite'
|
|
10
11
|
import { Fading } from '../Fading'
|
|
11
12
|
import { FallbackBoundary } from '../FallbackBoundary'
|
|
12
13
|
import { SelectorBox } from './styled'
|
|
@@ -22,11 +23,20 @@ interface Item {
|
|
|
22
23
|
visibility_level: SectionVisibility,
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
interface Favorite<T> {
|
|
27
|
+
listFavorites?: Array<T>,
|
|
28
|
+
onAddFavorite?: (idOrSlug?: string) => void,
|
|
29
|
+
onRemoveFavorite?: (idOrSlug?: string) => void,
|
|
30
|
+
pendingAddFav: boolean,
|
|
31
|
+
pendingRemoveFav: boolean,
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
interface ListProps<T> {
|
|
26
35
|
filter?: string,
|
|
27
36
|
visibility?: SectionVisibility,
|
|
28
37
|
selectorConfig: SelectorConfig<T>,
|
|
29
38
|
onSelect: (item: T) => void,
|
|
39
|
+
favorite?: Favorite<T>,
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
interface ListItemProps<T> extends ListProps<T> {
|
|
@@ -38,6 +48,7 @@ interface ContentProps<T> {
|
|
|
38
48
|
onClose: () => void,
|
|
39
49
|
inputRef: React.RefObject<HTMLTextAreaElement | HTMLInputElement>,
|
|
40
50
|
selectorConfig: SelectorConfig<T>,
|
|
51
|
+
favorite?: Favorite<T>,
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
interface SelectorConfig<T> {
|
|
@@ -57,9 +68,10 @@ interface SelectorConfig<T> {
|
|
|
57
68
|
interface SelectorProps<T> {
|
|
58
69
|
selectorConfig: SelectorConfig<T>,
|
|
59
70
|
inputRef: React.RefObject<HTMLTextAreaElement | HTMLInputElement>,
|
|
71
|
+
favorite?: Favorite<T>,
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
const ListItem = <T extends Item>({ item, selectorConfig, onSelect }: ListItemProps<T>) => {
|
|
74
|
+
const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }: ListItemProps<T>) => {
|
|
63
75
|
const t = useTranslate(dictionary)
|
|
64
76
|
const { urlBuilder, renderComponentItem } = selectorConfig
|
|
65
77
|
const linkTitle = interpolate(t.open, item.slug)
|
|
@@ -78,40 +90,54 @@ const ListItem = <T extends Item>({ item, selectorConfig, onSelect }: ListItemPr
|
|
|
78
90
|
<IconButton as="a" title={linkTitle} aria-label={linkTitle} href={`${getUrlToStackSpotAI()}${urlBuilder(item)}`} target="_blank">
|
|
79
91
|
<ExternalLink />
|
|
80
92
|
</IconButton>
|
|
93
|
+
<ButtonFavorite favorite={{ idOrSlug: item?.id || '', ...favorite }} isIconButton />
|
|
81
94
|
</li>
|
|
82
95
|
)
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
const List = <T extends Item>({ selectorConfig, filter, visibility, onSelect }: ListProps<T>) => {
|
|
98
|
+
const List = <T extends Item>({ selectorConfig, filter, visibility, onSelect, favorite }: ListProps<T>) => {
|
|
86
99
|
const t = useTranslate(dictionary)
|
|
87
100
|
const items = selectorConfig.data()
|
|
101
|
+
|
|
88
102
|
const filtered = useMemo(() => {
|
|
89
|
-
if (!filter && !visibility) return items
|
|
103
|
+
if (!filter && !visibility) return [...items]
|
|
90
104
|
const lowerFilter = filter?.toLowerCase() ?? ''
|
|
91
105
|
|
|
106
|
+
if (visibility?.toLowerCase() === 'favorite'){
|
|
107
|
+
return favorite?.listFavorites?.filter(item =>
|
|
108
|
+
(item[selectorConfig.searchProp] as string).toLocaleLowerCase().includes(lowerFilter))
|
|
109
|
+
}
|
|
110
|
+
|
|
92
111
|
return items.filter(item =>
|
|
93
112
|
(item[selectorConfig.searchProp] as string).toLocaleLowerCase().includes(lowerFilter) &&
|
|
94
113
|
(!visibility || item?.visibility_level?.toLowerCase() === visibility.toLowerCase()),
|
|
95
114
|
)
|
|
96
|
-
}, [filter, items, visibility])
|
|
115
|
+
}, [filter, items, visibility, favorite?.listFavorites])
|
|
116
|
+
|
|
97
117
|
|
|
98
118
|
if (!items.length) return <Text className="empty" colorScheme="light.700">{interpolate(t.noData, selectorConfig.resourceName)}</Text>
|
|
99
|
-
if (!filtered
|
|
119
|
+
if (!filtered?.length)
|
|
100
120
|
<Text className="empty" colorScheme="light.700">{interpolate(t.noResults, selectorConfig.resourceName)}</Text>
|
|
101
121
|
|
|
102
122
|
return (
|
|
103
123
|
<ul className="selector-list">
|
|
104
|
-
{filtered
|
|
105
|
-
<ListItem
|
|
124
|
+
{filtered?.map((item) => (
|
|
125
|
+
<ListItem
|
|
126
|
+
key={item.id}
|
|
127
|
+
item={item}
|
|
128
|
+
selectorConfig={selectorConfig}
|
|
129
|
+
onSelect={onSelect}
|
|
130
|
+
favorite={favorite}
|
|
131
|
+
/>
|
|
106
132
|
))}
|
|
107
133
|
</ul>
|
|
108
134
|
)
|
|
109
135
|
}
|
|
110
136
|
|
|
111
|
-
const SelectorContent = ({ filter, onClose, selectorConfig }: ContentProps<any>) => {
|
|
137
|
+
const SelectorContent = ({ filter, onClose, selectorConfig, favorite }: ContentProps<any>) => {
|
|
112
138
|
const t = useTranslate(dictionary)
|
|
113
139
|
const ref = useRef<HTMLDivElement>(null)
|
|
114
|
-
const [visibility, setVisibility] = useState<SectionVisibility | undefined>()
|
|
140
|
+
const [visibility, setVisibility] = useState<SectionVisibility | undefined>('favorite')
|
|
115
141
|
const { resourceName, icon, onSelect, sections } = selectorConfig
|
|
116
142
|
const onSelectItem = useCallback((slug: string) => {
|
|
117
143
|
onSelect(slug)
|
|
@@ -127,11 +153,11 @@ const SelectorContent = ({ filter, onClose, selectorConfig }: ContentProps<any>)
|
|
|
127
153
|
ref,
|
|
128
154
|
}, [])
|
|
129
155
|
|
|
130
|
-
function createSectionItem(action
|
|
156
|
+
function createSectionItem(action: SectionVisibility) {
|
|
131
157
|
return (
|
|
132
|
-
<li key={action
|
|
158
|
+
<li key={action}>
|
|
133
159
|
<button className={visibility === action ? 'active' : ''} onFocus={() => setVisibility(action)}>
|
|
134
|
-
{t[action
|
|
160
|
+
{t[action]}
|
|
135
161
|
</button>
|
|
136
162
|
</li>
|
|
137
163
|
)
|
|
@@ -145,17 +171,17 @@ const SelectorContent = ({ filter, onClose, selectorConfig }: ContentProps<any>)
|
|
|
145
171
|
</header>
|
|
146
172
|
<div className="body">
|
|
147
173
|
<ul className="tabs">
|
|
148
|
-
{createSectionItem
|
|
149
|
-
|
|
174
|
+
{sections.map(createSectionItem)}
|
|
175
|
+
</ul>
|
|
150
176
|
<FallbackBoundary message={interpolate(t.error, selectorConfig.resourceName)} mini>
|
|
151
|
-
<List filter={filter} visibility={visibility} selectorConfig={selectorConfig} onSelect={onSelectItem} />
|
|
177
|
+
<List filter={filter} visibility={visibility} selectorConfig={selectorConfig} onSelect={onSelectItem} favorite={favorite} />
|
|
152
178
|
</FallbackBoundary>
|
|
153
179
|
</div>
|
|
154
180
|
</div>
|
|
155
181
|
)
|
|
156
182
|
}
|
|
157
183
|
|
|
158
|
-
export const Selector = <T, >({ inputRef, selectorConfig }: SelectorProps<T>) => {
|
|
184
|
+
export const Selector = <T, >({ inputRef, selectorConfig, favorite }: SelectorProps<T>) => {
|
|
159
185
|
const { shortcut, regex, isEnabled } = selectorConfig
|
|
160
186
|
const [isClosed, setClosed] = useState(false)
|
|
161
187
|
const selectorRef = useRef<HTMLDivElement>(null)
|
|
@@ -210,6 +236,7 @@ export const Selector = <T, >({ inputRef, selectorConfig }: SelectorProps<T>) =>
|
|
|
210
236
|
<SelectorBox>
|
|
211
237
|
<Fading visible={shouldRender} ref={selectorRef} className="box-selector">
|
|
212
238
|
<SelectorContent
|
|
239
|
+
favorite={favorite}
|
|
213
240
|
selectorConfig={selectorConfig}
|
|
214
241
|
filter={filter}
|
|
215
242
|
onClose={() => setClosed(true)}
|
|
@@ -232,6 +259,7 @@ const dictionary = {
|
|
|
232
259
|
noData: 'You don\'t have any $0 yet.',
|
|
233
260
|
noResults: 'There are no $0s to show here.',
|
|
234
261
|
open: 'Open this $0 settings in a new tab.',
|
|
262
|
+
favorite: 'Favorites',
|
|
235
263
|
},
|
|
236
264
|
pt: {
|
|
237
265
|
all: 'Todos',
|
|
@@ -244,5 +272,6 @@ const dictionary = {
|
|
|
244
272
|
noData: 'Você ainda não possui $0s.',
|
|
245
273
|
noResults: 'Não $0s para mostrar aqui.',
|
|
246
274
|
open: 'Abra as configurações deste $0 em uma nova aba.',
|
|
275
|
+
favorite: 'Favoritos',
|
|
247
276
|
},
|
|
248
277
|
} satisfies Dictionary
|
|
@@ -9,12 +9,23 @@ import { CheckProps } from './types'
|
|
|
9
9
|
* Renders a checkbox group where each option has a label and a description.
|
|
10
10
|
* The description in placed under the label and checkbox as an accordion.
|
|
11
11
|
*/
|
|
12
|
-
export function DescribedCheckboxGroup<T>(
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export function DescribedCheckboxGroup<T>({
|
|
13
|
+
keygen,
|
|
14
|
+
onChange,
|
|
15
|
+
options,
|
|
16
|
+
renderDescription,
|
|
17
|
+
renderLabel,
|
|
18
|
+
renderBeforeElement,
|
|
19
|
+
renderAfterElement,
|
|
20
|
+
optionClassName,
|
|
21
|
+
optionStyle,
|
|
22
|
+
value,
|
|
23
|
+
className,
|
|
24
|
+
style }: CheckProps<T>) {
|
|
15
25
|
const items = useMemo(() => options.map((option) => {
|
|
16
26
|
const label = renderLabel(option)
|
|
17
27
|
const description = renderDescription(option)
|
|
28
|
+
|
|
18
29
|
const header = (
|
|
19
30
|
<label>
|
|
20
31
|
<Checkbox
|
|
@@ -27,15 +38,20 @@ export function DescribedCheckboxGroup<T>(
|
|
|
27
38
|
{typeof label === 'string' ? <Text>{label}</Text> : label}
|
|
28
39
|
</label>
|
|
29
40
|
)
|
|
41
|
+
|
|
30
42
|
return (
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
<>
|
|
44
|
+
<li key={keygen(option)} className={listToClass(['radio-group-item', optionClassName?.(option)])} style={optionStyle?.(option)}>
|
|
45
|
+
{renderBeforeElement?.(option)}
|
|
46
|
+
<Accordion header={header}>
|
|
47
|
+
{typeof description === 'string'
|
|
48
|
+
? <Text appearance="microtext1" colorScheme="light.700">{description}</Text>
|
|
49
|
+
: description as any
|
|
50
|
+
}
|
|
51
|
+
</Accordion>
|
|
52
|
+
{renderAfterElement?.(option)}
|
|
53
|
+
</li>
|
|
54
|
+
</>
|
|
39
55
|
)
|
|
40
56
|
}), [options, value])
|
|
41
57
|
|
|
@@ -10,11 +10,24 @@ import { RadioProps } from './types'
|
|
|
10
10
|
* The description in placed under the label and radio button as an accordion.
|
|
11
11
|
*/
|
|
12
12
|
export function DescribedRadioGroup<T>(
|
|
13
|
-
{
|
|
13
|
+
{
|
|
14
|
+
keygen,
|
|
15
|
+
onChange,
|
|
16
|
+
options,
|
|
17
|
+
renderDescription,
|
|
18
|
+
renderLabel,
|
|
19
|
+
renderBeforeElement,
|
|
20
|
+
renderAfterElement,
|
|
21
|
+
optionClassName,
|
|
22
|
+
optionStyle,
|
|
23
|
+
value,
|
|
24
|
+
className,
|
|
25
|
+
style }: RadioProps<T>,
|
|
14
26
|
) {
|
|
15
27
|
const items = useMemo(() => options.map((option) => {
|
|
16
28
|
const label = renderLabel(option)
|
|
17
29
|
const description = renderDescription(option)
|
|
30
|
+
|
|
18
31
|
const header = (
|
|
19
32
|
<label>
|
|
20
33
|
<Radio checked={option === value} onChange={() => onChange(option)} />
|
|
@@ -23,12 +36,14 @@ export function DescribedRadioGroup<T>(
|
|
|
23
36
|
)
|
|
24
37
|
return (
|
|
25
38
|
<li key={keygen(option)} className={listToClass(['radio-group-item', optionClassName?.(option)])} style={optionStyle?.(option)}>
|
|
39
|
+
{renderBeforeElement?.(option)}
|
|
26
40
|
<Accordion header={header}>
|
|
27
41
|
{typeof description === 'string'
|
|
28
42
|
? <Text appearance="microtext1" colorScheme="light.700">{description}</Text>
|
|
29
43
|
: description as any
|
|
30
44
|
}
|
|
31
45
|
</Accordion>
|
|
46
|
+
{renderAfterElement?.(option)}
|
|
32
47
|
</li>
|
|
33
48
|
)
|
|
34
49
|
}), [options, value])
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const dictionaryFavorites = {
|
|
2
|
+
en: {
|
|
3
|
+
add: 'Add',
|
|
4
|
+
remove: 'Remove',
|
|
5
|
+
become: 'Become',
|
|
6
|
+
favorite: 'Favorite',
|
|
7
|
+
couldNotActFavorite: 'Could not $0 favorite',
|
|
8
|
+
actionFavoriteSuccessfully: '$0 from favorites successfully',
|
|
9
|
+
},
|
|
10
|
+
pt: {
|
|
11
|
+
add: 'Adicionar',
|
|
12
|
+
remove: 'Remover',
|
|
13
|
+
become: 'Tornar',
|
|
14
|
+
favorite: 'Favorito',
|
|
15
|
+
couldNotActFavorite: 'Não foi possível $0 o favorito',
|
|
16
|
+
actionFavoriteSuccessfully: '$0 dos favoritos com sucesso',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
@@ -13,6 +13,14 @@ interface RadioCheckProps<T> extends WithStyle {
|
|
|
13
13
|
* A function that renders an option as a description. This can either return a string or a React Element.
|
|
14
14
|
*/
|
|
15
15
|
renderDescription: (option: T) => React.ReactNode,
|
|
16
|
+
/**
|
|
17
|
+
* A function that renders a element before the content
|
|
18
|
+
*/
|
|
19
|
+
renderBeforeElement?: (option: T) => React.ReactNode,
|
|
20
|
+
/**
|
|
21
|
+
* A function that renders a element after the content
|
|
22
|
+
*/
|
|
23
|
+
renderAfterElement?: (option: T) => React.ReactNode,
|
|
16
24
|
/**
|
|
17
25
|
* A function that gives a custom className to the rendered option.
|
|
18
26
|
*/
|
|
@@ -25,7 +33,9 @@ interface RadioCheckProps<T> extends WithStyle {
|
|
|
25
33
|
* A function that generates a unique id for the option.
|
|
26
34
|
*/
|
|
27
35
|
keygen: (option: T) => React.Key,
|
|
28
|
-
|
|
36
|
+
/**
|
|
37
|
+
* An object containing favorite items and related operations.
|
|
38
|
+
*/
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
export interface RadioProps<T> extends RadioCheckProps<T> {
|
|
@@ -14,9 +14,11 @@ export const AgentsPanel = () => {
|
|
|
14
14
|
const isTrial = checkIsTrial()
|
|
15
15
|
|
|
16
16
|
const tabs= useMemo(() => isTrial ? [
|
|
17
|
+
{ title: t.favorites, content: <AgentsTab key="favorite" visibility="FAVORITE" /> },
|
|
17
18
|
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
|
|
18
19
|
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
19
20
|
]: [
|
|
21
|
+
{ title: t.favorites, content: <AgentsTab key="favorite" visibility="FAVORITE" /> },
|
|
20
22
|
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
|
|
21
23
|
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
22
24
|
{ title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" /> },
|
|
@@ -5,6 +5,7 @@ import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
|
5
5
|
import { agentClient } from '@stack-spot/portal-network'
|
|
6
6
|
import { AgentResponse, VisibilityLevel } from '@stack-spot/portal-network/api/agent'
|
|
7
7
|
import { useMemo, useState } from 'react'
|
|
8
|
+
import { ButtonFavorite } from '../../components/ButtonFavorite'
|
|
8
9
|
import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
|
|
9
10
|
import { IconInput } from '../../components/IconInput'
|
|
10
11
|
import { useCurrentChat } from '../../context/hooks'
|
|
@@ -13,21 +14,27 @@ import { isAgentDefault } from '../../utils/agent'
|
|
|
13
14
|
import { AgentDescription } from './AgentDescription'
|
|
14
15
|
import { useAgentsDictionary } from './dictionary'
|
|
15
16
|
import { AgentLabel } from './styled'
|
|
17
|
+
import { useAgentFavorites } from './useAgentFavorites'
|
|
16
18
|
|
|
17
19
|
export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' }) => {
|
|
18
20
|
const t = useAgentsDictionary()
|
|
19
21
|
const { close } = useRightPanel()
|
|
20
22
|
const chat = useCurrentChat()
|
|
21
23
|
const [filter, setFilter] = useState('')
|
|
24
|
+
const { listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav } = useAgentFavorites()
|
|
25
|
+
|
|
22
26
|
const agentsBuiltIn = agentClient.publicAgents.useQuery({})
|
|
23
27
|
const agentDefault = agentsBuiltIn.find((agent) => isAgentDefault(agent.slug))
|
|
24
28
|
const agents = visibility === 'BUILT-IN' ? agentsBuiltIn : agentClient.agents.useQuery({ visibility })
|
|
29
|
+
|
|
25
30
|
const [value, setValue] = useState<AgentResponse | undefined>(
|
|
26
31
|
chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault,
|
|
27
32
|
)
|
|
33
|
+
|
|
28
34
|
const filtered = useMemo(
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
// Recreate the list so that the favorites list is taken into account
|
|
36
|
+
() => filter ? agents.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : [...agents],
|
|
37
|
+
[agents, filter, value, listFavorites],
|
|
31
38
|
)
|
|
32
39
|
|
|
33
40
|
function submit() {
|
|
@@ -43,6 +50,9 @@ export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT
|
|
|
43
50
|
<IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
|
|
44
51
|
{!!filtered.length && <DescribedRadioGroup
|
|
45
52
|
options={filtered}
|
|
53
|
+
renderAfterElement={({ id }) => (
|
|
54
|
+
<ButtonFavorite favorite={{ idOrSlug: id, listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav }} />
|
|
55
|
+
)}
|
|
46
56
|
keygen={a => a.id}
|
|
47
57
|
value={value}
|
|
48
58
|
onChange={setValue}
|
|
@@ -15,6 +15,7 @@ const dictionary = {
|
|
|
15
15
|
noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
|
|
16
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
17
|
description: 'Description',
|
|
18
|
+
favorites: 'Favorites',
|
|
18
19
|
tools: 'Tools',
|
|
19
20
|
},
|
|
20
21
|
pt: {
|
|
@@ -31,6 +32,7 @@ const dictionary = {
|
|
|
31
32
|
noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
|
|
32
33
|
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.',
|
|
33
34
|
description: 'Descrição',
|
|
35
|
+
favorites: 'Favoritos',
|
|
34
36
|
tools: 'Ferramentas',
|
|
35
37
|
},
|
|
36
38
|
} satisfies Dictionary
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable filenames/match-regex */
|
|
2
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
3
|
+
|
|
4
|
+
export function useAgentFavorites() {
|
|
5
|
+
const listFavorites = agentClient.agents.useQuery({ visibility: 'FAVORITE' })
|
|
6
|
+
const [addFavorite, pendingAddFav] = agentClient.addFavoriteAgent.useMutation()
|
|
7
|
+
const [removeFavorite, pendingRemoveFav] = agentClient.removeFavoriteAgent.useMutation()
|
|
8
|
+
|
|
9
|
+
const onRemoveFavorite = async (idOrSlug?: string) => {
|
|
10
|
+
try {
|
|
11
|
+
await removeFavorite({ agentId: idOrSlug || '' })
|
|
12
|
+
await agentClient.agents.invalidate()
|
|
13
|
+
} catch (error) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.error(error)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const onAddFavorite = async (idOrSlug?: string) => {
|
|
20
|
+
try {
|
|
21
|
+
await addFavorite({ agentId: idOrSlug || '' })
|
|
22
|
+
await agentClient.agents.invalidate()
|
|
23
|
+
} catch (error) {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.error(error)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -168,8 +168,8 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
|
|
|
168
168
|
const chat = useCurrentChat()
|
|
169
169
|
const agentId = entry.agent?.id ?? ''
|
|
170
170
|
const [agentById] = agentClient.agent.useStatefulQuery({ agentId }, { enabled: !!agentId && !entry?.agent?.builtIn })
|
|
171
|
-
const [
|
|
172
|
-
const agent = agentById ??
|
|
171
|
+
const [agentBuiltInById] = agentClient.publicAgent.useStatefulQuery({ agentId }, { enabled: !!agentId && entry?.agent?.builtIn })
|
|
172
|
+
const agent = agentById ?? agentBuiltInById
|
|
173
173
|
|
|
174
174
|
useChatScrollToBottomEffect(ref, [entry])
|
|
175
175
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { agentClient, aiClient,
|
|
1
|
+
import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
|
|
2
2
|
import { ChatProperties } from '../../state/ChatState'
|
|
3
3
|
import { LabeledWithImage } from '../../state/types'
|
|
4
4
|
|
|
@@ -27,7 +27,8 @@ export async function findStack(id: string | null): Promise<ChatProperties['stac
|
|
|
27
27
|
export async function findWorkspace(id: string | null): Promise<ChatProperties['workspace'] | undefined> {
|
|
28
28
|
if (!id) return
|
|
29
29
|
try {
|
|
30
|
-
const ws = await workspaceAiClient.workspaceAi.query({ id })
|
|
30
|
+
// const ws = await workspaceAiClient.workspaceAi.query({ id })
|
|
31
|
+
const ws = await workspaceClient.workspace.query({ workspaceId: id })
|
|
31
32
|
return { id, label: ws.name }
|
|
32
33
|
} catch (error) {
|
|
33
34
|
// eslint-disable-next-line no-console
|