@stack-spot/ai-chat-widget 3.4.0-beta.5 → 3.5.0-beta.5

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 (38) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/app-metadata.json +4 -4
  3. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  4. package/dist/chat-interceptors/send-message.js +1 -0
  5. package/dist/chat-interceptors/send-message.js.map +1 -1
  6. package/dist/state/ChatEntry.d.ts +10 -1
  7. package/dist/state/ChatEntry.d.ts.map +1 -1
  8. package/dist/state/ChatEntry.js +16 -2
  9. package/dist/state/ChatEntry.js.map +1 -1
  10. package/dist/views/Chat/ChatMessage.d.ts +1 -1
  11. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  12. package/dist/views/Chat/ChatMessage.js +3 -2
  13. package/dist/views/Chat/ChatMessage.js.map +1 -1
  14. package/dist/views/Chat/StepsList.js +1 -1
  15. package/dist/views/Chat/StepsList.js.map +1 -1
  16. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  17. package/dist/views/ChatHistory/utils.js +94 -2
  18. package/dist/views/ChatHistory/utils.js.map +1 -1
  19. package/dist/views/KnowledgeSources.d.ts +1 -1
  20. package/dist/views/KnowledgeSources.d.ts.map +1 -1
  21. package/dist/views/KnowledgeSources.js +35 -7
  22. package/dist/views/KnowledgeSources.js.map +1 -1
  23. package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
  24. package/dist/views/MessageInput/QuickCommandSelector.js +52 -29
  25. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
  26. package/dist/views/MessageInput/UploadBar.d.ts.map +1 -1
  27. package/dist/views/MessageInput/UploadBar.js +0 -5
  28. package/dist/views/MessageInput/UploadBar.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/app-metadata.json +4 -4
  31. package/src/chat-interceptors/send-message.ts +1 -0
  32. package/src/state/ChatEntry.ts +18 -2
  33. package/src/views/Chat/ChatMessage.tsx +4 -2
  34. package/src/views/Chat/StepsList.tsx +1 -1
  35. package/src/views/ChatHistory/utils.ts +96 -3
  36. package/src/views/KnowledgeSources.tsx +60 -17
  37. package/src/views/MessageInput/QuickCommandSelector.tsx +93 -39
  38. package/src/views/MessageInput/UploadBar.tsx +0 -6
@@ -1,3 +1,4 @@
1
+ import { Box } from '@citric/core'
1
2
  import { Button, Tab } from '@stack-spot/citric-react'
2
3
  import { Placeholder } from '@stack-spot/portal-components/Placeholder'
3
4
  import { aiClient, dataIntegrationClient, workspaceAiClient } from '@stack-spot/portal-network'
@@ -5,6 +6,7 @@ import { VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
5
6
  import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
6
7
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
8
  import React, { useCallback, useEffect, useMemo, useRef } from 'react'
9
+ import InfiniteScroll from 'react-infinite-scroll-component'
8
10
  import { NavigationComponent } from '../components/ComponentNavigator'
9
11
  import { DescribedCheckboxGroup } from '../components/form/DescribedCheckboxGroup'
10
12
  import { RightPanelContentList } from '../components/RightPanelContentList'
@@ -95,15 +97,46 @@ const KnowledgeSourcesPanel = () => {
95
97
 
96
98
  export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId, showSubmitButton = true }: TabProps) => {
97
99
  const t = useTranslate(dictionary)
98
- const knowledgeSources = workspaceId
99
- ? workspaceAiClient.getKSFromWorkspaceAi.useQuery({ workspaceId })
100
- : aiClient.knowledgeSources.useQuery({
101
- visibility, order: 'a-to-z', types: ['snippet', 'api', 'event', 'custom'],
102
- })
100
+
101
+ let knowledgeSources: any[] = []
102
+ let fetchNextPage: (() => void) | undefined
103
+ let hasNextPage: boolean | undefined
104
+ let isLoadingKnowledgeSources: boolean | undefined
105
+ let ksListError: any
106
+
107
+ if (workspaceId) {
108
+ knowledgeSources = workspaceAiClient.getKSFromWorkspaceAi.useQuery({ workspaceId })
109
+ fetchNextPage = undefined
110
+ hasNextPage = false
111
+ isLoadingKnowledgeSources = false
112
+ ksListError = undefined
113
+ } else {
114
+ const [
115
+ ksListPages,
116
+ _isLoadingKnowledgeSources,
117
+ _ksListError,
118
+ { fetchNextPage: _fetchNextPage, hasNextPage: _hasNextPage },
119
+ ] = dataIntegrationClient.knowledgeSourcesV2.useStatefulInfiniteQuery(
120
+ {
121
+ order: 'a-to-z',
122
+ visibilityList: visibility ? [visibility] : [],
123
+ types: ['snippet', 'api', 'event', 'custom'],
124
+ size: 20,
125
+ },
126
+ { enabled: true },
127
+ )
128
+ knowledgeSources = ksListPages?.flat() ?? []
129
+ fetchNextPage = _fetchNextPage
130
+ hasNextPage = _hasNextPage
131
+ isLoadingKnowledgeSources = _isLoadingKnowledgeSources
132
+ ksListError = _ksListError
133
+ }
134
+
103
135
  const initialValue = useMemo(() => {
104
136
  const currentlySelected = allKS.current?.map(ks => ks.id)
105
137
  return knowledgeSources.filter(ks => currentlySelected?.includes(ks.slug))
106
- }, [])
138
+ }, [knowledgeSources, allKS])
139
+
107
140
  const listFavorites = dataIntegrationClient.knowledgeSources.useQuery({ visibility: 'favorite' })
108
141
  const [addFavorite, pendingAddFav] = dataIntegrationClient.addFavoriteKnowledgeSource.useMutation()
109
142
  const [removeFavorite, pendingRemoveFav] = dataIntegrationClient.removeFavoriteKnowledgeSource.useMutation()
@@ -134,6 +167,9 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
134
167
  }
135
168
  })
136
169
 
170
+ if (isLoadingKnowledgeSources && knowledgeSources.length === 0) {return null}
171
+ if (ksListError) {return null}
172
+
137
173
  const addFavoriteKs = async (idOrSlug: string) => {
138
174
  try {
139
175
  await addFavorite({ slug: idOrSlug })
@@ -162,16 +198,24 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
162
198
 
163
199
  return (
164
200
  <>
165
- <div className="content">
166
- <DescribedCheckboxGroup
167
- options={knowledgeSources}
168
- initialValue={initialValue}
169
- globalSelection={allKS}
170
- data={ks => ({ idOrSlug: ks.slug, description: ks.description, name: ks.name, listFavorites, onAddFavorite, onRemoveFavorite })}
171
- emptyResults={<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
172
- emptyDataset={<Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
173
- />
174
- </div>
201
+ <Box className="content" id="ks-scrollable" style={{ overflow: 'auto', height: '100%' }}>
202
+ <InfiniteScroll
203
+ scrollableTarget="ks-scrollable"
204
+ dataLength={knowledgeSources.length}
205
+ next={fetchNextPage ?? (() => {})}
206
+ hasMore={hasNextPage}
207
+ loader={isLoadingKnowledgeSources}
208
+ >
209
+ <DescribedCheckboxGroup
210
+ options={knowledgeSources}
211
+ initialValue={initialValue}
212
+ globalSelection={allKS}
213
+ data={ks => ({ idOrSlug: ks.slug, description: ks.description, name: ks.name, listFavorites, onAddFavorite, onRemoveFavorite })}
214
+ emptyResults={<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
215
+ emptyDataset={<Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
216
+ />
217
+ </InfiniteScroll>
218
+ </Box>
175
219
  {!!knowledgeSources.length && showSubmitButton && <Button onClick={onSubmit}>{t.apply}</Button>}
176
220
  </>
177
221
  )
@@ -188,7 +232,6 @@ export function KnowledgeSourcesTabWorkspace({ allKS, onSubmit }: TabProps) {
188
232
  return <WorkspaceTabNavigator components={workspaceTabComponents} getNavigateParam={buildNavigateParams} />
189
233
  }
190
234
 
191
-
192
235
  const dictionary = {
193
236
  en: {
194
237
  title: 'Knowledge Sources',
@@ -1,7 +1,9 @@
1
+ import { Box } from '@citric/core'
1
2
  import { Icon } from '@stack-spot/citric-icons'
2
3
  import { aiClient, workspaceAiClient } from '@stack-spot/portal-network'
3
4
  import { QuickCommandResponse } from '@stack-spot/portal-network/api/ai'
4
- import { useCallback } from 'react'
5
+ import { useCallback, useMemo } from 'react'
6
+ import InfiniteScroll from 'react-infinite-scroll-component'
5
7
  import { Selector } from '../../components/Selector'
6
8
  import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../context/hooks'
7
9
  import { quickCommandRegex } from '../../regex'
@@ -77,51 +79,103 @@ export const QuickCommandSelector = ({ inputRef, isTrial }:
77
79
  inputRef.current.focus()
78
80
  }, [chat, inputRef])
79
81
 
80
- const getQuickCommands = () => {
81
- if (spotId) {
82
- return workspaceAiClient.getQCFromWorkspaceAi.useQuery({ workspaceId: spotId })
83
- }
84
-
85
- const quickCommands = aiClient.allQuickCommands.useQuery({ order: 'a-to-z' })
86
- const quickCommandsFiltered = quickCommands.filter(
87
- (qc) => qc.visibility_level.toLowerCase() !== 'workspace',
82
+ const [quickCommandsPages = [], isLoadingQuickCommands, _qcListError, infiniteQueryResult,
83
+ ] = aiClient.allQuickCommandsV3.useStatefulInfiniteQuery(
84
+ {
85
+ size: 20,
86
+ page: 1,
87
+ order: 'a-to-z',
88
+ },
89
+ { enabled: !spotId },
90
+ )
91
+
92
+ const quickCommands = useMemo(() => quickCommandsPages, [quickCommandsPages])
93
+
94
+ const quickCommandsFiltered = useMemo(() => quickCommands.filter(
95
+ (qc) => qc.visibility_level.toLowerCase() !== 'workspace'), [quickCommands],
96
+ )
97
+
98
+ const workspaceQuickCommands = workspaceAiClient.workspacesContentsByType.useQuery({
99
+ contentType: 'quick_command',
100
+ }) || []
101
+
102
+ const workspaceQuickCommandsWithWorkspaceName: QuickCommandResponseWithSpaceName[] =
103
+ useMemo(
104
+ () =>
105
+ workspaceQuickCommands.flatMap(({ qcs, space_name }) =>
106
+ qcs?.map((qc) => ({ ...qc, spaceName: space_name })),
107
+ ) as QuickCommandResponseWithSpaceName[],
108
+ [workspaceQuickCommands],
88
109
  )
89
110
 
90
- const workspaceQuickCommands = workspaceAiClient.workspacesContentsByType.useQuery({
91
- contentType: 'quick_command',
92
- })
111
+ const allQuickCommands =
112
+ spotId
113
+ ? workspaceAiClient.getQCFromWorkspaceAi.useQuery({ workspaceId: spotId }) || []
114
+ : [...quickCommandsFiltered, ...workspaceQuickCommandsWithWorkspaceName]
93
115
 
94
- const workspaceQuickCommandsWithWorkspaceName: QuickCommandResponseWithSpaceName[] =
95
- workspaceQuickCommands.flatMap(({ qcs, space_name }) =>
96
- qcs?.map((qc) => ({ ...qc, spaceName: space_name })),
97
- ) as QuickCommandResponseWithSpaceName[]
116
+ const fetchNextPage = !spotId ? infiniteQueryResult?.fetchNextPage : undefined
117
+ const hasNextPage = !spotId ? infiniteQueryResult?.hasNextPage : false
98
118
 
99
- return [...quickCommandsFiltered, ...workspaceQuickCommandsWithWorkspaceName]
100
- }
101
-
102
- const QuickCommandItem = ({ slug, description, spaceName }: QuickCommandResponseWithSpaceName) => <>
119
+ const QuickCommandItem = ({ slug, description, spaceName }: QuickCommandResponseWithSpaceName) => (<>
103
120
  <p className="selector-description">{spaceName}</p>
104
121
  <p className="selector-title">/{slug?.toUpperCase()}</p>
105
122
  <p className="selector-description">{description}</p>
106
123
  </>
124
+ )
125
+
126
+ if (spotId) {
127
+ return (
128
+ <Selector
129
+ inputRef={inputRef}
130
+ favorite={{
131
+ useFavorites, onAddFavorite, onRemoveFavorite, favoriteIsSlug: true,
132
+ }}
133
+ selectorConfig={{
134
+ resourceName: 'Quick Command',
135
+ shortcut: '/',
136
+ icon: <Icon icon="QuickCommand" />,
137
+ searchProp: 'slug',
138
+ urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
139
+ regex: quickCommandRegex,
140
+ sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
141
+ isEnabled: isQuickCommandEnabled,
142
+ onSelect: onSelectItem,
143
+ renderComponentItem: QuickCommandItem,
144
+ useData: () => allQuickCommands,
145
+ }}
146
+ />
147
+ )
148
+ }
107
149
 
108
- return <Selector
109
- inputRef={inputRef}
110
- favorite={{
111
- useFavorites, onAddFavorite, onRemoveFavorite, favoriteIsSlug: true,
112
- }}
113
- selectorConfig={{
114
- resourceName: 'Quick Command',
115
- shortcut: '/',
116
- icon: <Icon icon="QuickCommand" />,
117
- searchProp: 'slug',
118
- urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
119
- regex: quickCommandRegex,
120
- sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
121
- isEnabled: isQuickCommandEnabled,
122
- onSelect: onSelectItem,
123
- renderComponentItem: QuickCommandItem,
124
- useData: getQuickCommands,
125
- }}
126
- />
150
+ return (
151
+ <Box className="content" id="qc-scrollable" style={{ overflow: 'auto', height: '100%' }}>
152
+ <InfiniteScroll
153
+ scrollableTarget="qc-scrollable"
154
+ dataLength={allQuickCommands.length}
155
+ next={fetchNextPage as any}
156
+ hasMore={!!hasNextPage}
157
+ loader={isLoadingQuickCommands}
158
+ >
159
+ <Selector
160
+ inputRef={inputRef}
161
+ favorite={{
162
+ useFavorites, onAddFavorite, onRemoveFavorite, favoriteIsSlug: true,
163
+ }}
164
+ selectorConfig={{
165
+ resourceName: 'Quick Command',
166
+ shortcut: '/',
167
+ icon: <Icon icon="QuickCommand" />,
168
+ searchProp: 'slug',
169
+ urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
170
+ regex: quickCommandRegex,
171
+ sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
172
+ isEnabled: isQuickCommandEnabled,
173
+ onSelect: onSelectItem,
174
+ renderComponentItem: QuickCommandItem,
175
+ useData: () => allQuickCommands,
176
+ }}
177
+ />
178
+ </InfiniteScroll>
179
+ </Box>
180
+ )
127
181
  }
@@ -80,12 +80,6 @@ export const UploadBar = () => {
80
80
  setAriaMessage(lines.join(' '))
81
81
  setTimeout(() => setAriaMessage(''), 2000)
82
82
  chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
83
-
84
- if (lines.length) {
85
- setAriaMessage(lines.join(' '))
86
- setTimeout(() => setAriaMessage(''), 2000)
87
- chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
88
- }
89
83
  })
90
84
 
91
85
  return (