@budibase/frontend-core 3.27.4 → 3.28.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/package.json +2 -2
- package/src/api/index.ts +2 -0
- package/src/api/types.ts +2 -0
- package/src/api/user.ts +11 -7
- package/src/api/workspaceHome.ts +25 -0
- package/src/components/Chatbox/index.svelte +106 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.28.0",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -24,5 +24,5 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"vitest": "^3.2.4"
|
|
26
26
|
},
|
|
27
|
-
"gitHead": "
|
|
27
|
+
"gitHead": "b57235144ae6ed94b1b54d9ea73335106feedc02"
|
|
28
28
|
}
|
package/src/api/index.ts
CHANGED
|
@@ -54,6 +54,7 @@ import { buildWorkspaceAppEndpoints } from "./workspaceApps"
|
|
|
54
54
|
import { buildResourceEndpoints } from "./resource"
|
|
55
55
|
import { buildDeploymentEndpoints } from "./deploy"
|
|
56
56
|
import { buildWorkspaceFavouriteEndpoints } from "./workspaceFavourites"
|
|
57
|
+
import { buildWorkspaceHomeEndpoints } from "./workspaceHome"
|
|
57
58
|
import { buildRecaptchaEndpoints } from "./recaptcha"
|
|
58
59
|
import { buildAIConfigEndpoints } from "./aiConfig"
|
|
59
60
|
import { buildVectorDbEndpoints } from "./vectorDbs"
|
|
@@ -324,6 +325,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
|
|
|
324
325
|
navigation: buildNavigationEndpoints(API),
|
|
325
326
|
workspaceApp: buildWorkspaceAppEndpoints(API),
|
|
326
327
|
workspace: buildWorkspaceFavouriteEndpoints(API),
|
|
328
|
+
workspaceHome: buildWorkspaceHomeEndpoints(API),
|
|
327
329
|
resource: buildResourceEndpoints(API),
|
|
328
330
|
recaptcha: buildRecaptchaEndpoints(API),
|
|
329
331
|
aiConfig: buildAIConfigEndpoints(API),
|
package/src/api/types.ts
CHANGED
|
@@ -41,6 +41,7 @@ import { WorkspaceAppEndpoints } from "./workspaceApps"
|
|
|
41
41
|
import { ResourceEndpoints } from "./resource"
|
|
42
42
|
import { DeploymentEndpoints } from "./deploy"
|
|
43
43
|
import { WorkspaceFavouriteEndpoints } from "./workspaceFavourites"
|
|
44
|
+
import { WorkspaceHomeEndpoints } from "./workspaceHome"
|
|
44
45
|
import { RecaptchaEndpoints } from "./recaptcha"
|
|
45
46
|
import { AIConfigEndpoints } from "./aiConfig"
|
|
46
47
|
import { VectorDbEndpoints } from "./vectorDbs"
|
|
@@ -155,6 +156,7 @@ export type APIClient = BaseAPIClient &
|
|
|
155
156
|
navigation: NavigationEndpoints
|
|
156
157
|
workspaceApp: WorkspaceAppEndpoints
|
|
157
158
|
workspace: WorkspaceFavouriteEndpoints
|
|
159
|
+
workspaceHome: WorkspaceHomeEndpoints
|
|
158
160
|
deployment: DeploymentEndpoints
|
|
159
161
|
recaptcha: RecaptchaEndpoints
|
|
160
162
|
aiConfig: AIConfigEndpoints
|
package/src/api/user.ts
CHANGED
|
@@ -43,7 +43,10 @@ export interface UserEndpoints {
|
|
|
43
43
|
deleteUser: (userId: string) => Promise<DeleteUserResponse>
|
|
44
44
|
deleteUsers: (users: UserIdentifier[]) => Promise<BulkUserDeleted | undefined>
|
|
45
45
|
onboardUsers: (data: InviteUsersRequest) => Promise<InviteUsersResponse>
|
|
46
|
-
getUserInvite: (
|
|
46
|
+
getUserInvite: (
|
|
47
|
+
code: string,
|
|
48
|
+
tenantId?: string
|
|
49
|
+
) => Promise<CheckInviteResponse>
|
|
47
50
|
getUserInvites: () => Promise<GetUserInvitesResponse>
|
|
48
51
|
inviteUsers: (users: InviteUsersRequest) => Promise<InviteUsersResponse>
|
|
49
52
|
removeUserInvites: (
|
|
@@ -52,7 +55,7 @@ export interface UserEndpoints {
|
|
|
52
55
|
acceptInvite: (
|
|
53
56
|
data: AcceptUserInviteRequest
|
|
54
57
|
) => Promise<AcceptUserInviteResponse>
|
|
55
|
-
|
|
58
|
+
getUserCountByWorkspace: (workspaceId: string) => Promise<number>
|
|
56
59
|
getAccountHolder: () => Promise<LookupAccountHolderResponse>
|
|
57
60
|
searchUsers: (data: SearchUsersRequest) => Promise<SearchUsersResponse>
|
|
58
61
|
createUsers: (
|
|
@@ -223,9 +226,10 @@ export const buildUserEndpoints = (API: BaseAPIClient): UserEndpoints => ({
|
|
|
223
226
|
* Retrieves the invitation associated with a provided code.
|
|
224
227
|
* @param code The unique code for the target invite
|
|
225
228
|
*/
|
|
226
|
-
getUserInvite: async code => {
|
|
229
|
+
getUserInvite: async (code, tenantId) => {
|
|
230
|
+
const query = tenantId ? `?tenantId=${encodeURIComponent(tenantId)}` : ""
|
|
227
231
|
return await API.get({
|
|
228
|
-
url: `/api/global/users/invite/${code}`,
|
|
232
|
+
url: `/api/global/users/invite/${code}${query}`,
|
|
229
233
|
})
|
|
230
234
|
},
|
|
231
235
|
|
|
@@ -270,11 +274,11 @@ export const buildUserEndpoints = (API: BaseAPIClient): UserEndpoints => ({
|
|
|
270
274
|
},
|
|
271
275
|
|
|
272
276
|
/**
|
|
273
|
-
* Counts the number of users in
|
|
277
|
+
* Counts the number of users in a workspace
|
|
274
278
|
*/
|
|
275
|
-
|
|
279
|
+
getUserCountByWorkspace: async workspaceId => {
|
|
276
280
|
const res = await API.get<CountUserResponse>({
|
|
277
|
-
url: `/api/global/users/count/${
|
|
281
|
+
url: `/api/global/users/count/${workspaceId}`,
|
|
278
282
|
})
|
|
279
283
|
return res.userCount
|
|
280
284
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GetGitHubStarsResponse,
|
|
3
|
+
GetWorkspaceHomeMetricsResponse,
|
|
4
|
+
} from "@budibase/types"
|
|
5
|
+
import { BaseAPIClient } from "./types"
|
|
6
|
+
|
|
7
|
+
export interface WorkspaceHomeEndpoints {
|
|
8
|
+
getMetrics: () => Promise<GetWorkspaceHomeMetricsResponse>
|
|
9
|
+
getGitHubStars: () => Promise<GetGitHubStarsResponse>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const buildWorkspaceHomeEndpoints = (
|
|
13
|
+
API: BaseAPIClient
|
|
14
|
+
): WorkspaceHomeEndpoints => ({
|
|
15
|
+
getMetrics: async () => {
|
|
16
|
+
return await API.get({
|
|
17
|
+
url: "/api/workspace/home/metrics",
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
getGitHubStars: async () => {
|
|
21
|
+
return await API.get({
|
|
22
|
+
url: "/api/global/github/stars",
|
|
23
|
+
})
|
|
24
|
+
},
|
|
25
|
+
})
|
|
@@ -31,10 +31,13 @@
|
|
|
31
31
|
chat: ChatConversationLike
|
|
32
32
|
persistConversation?: boolean
|
|
33
33
|
conversationStarters?: { prompt: string }[]
|
|
34
|
+
initialPrompt?: string
|
|
34
35
|
onchatsaved?: (_event: {
|
|
35
36
|
detail: { chatId?: string; chat: ChatConversationLike }
|
|
36
37
|
}) => void
|
|
37
38
|
isAgentPreviewChat?: boolean
|
|
39
|
+
readOnly?: boolean
|
|
40
|
+
readOnlyReason?: "disabled" | "deleted" | "offline"
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
let {
|
|
@@ -42,8 +45,11 @@
|
|
|
42
45
|
chat = $bindable(),
|
|
43
46
|
persistConversation = true,
|
|
44
47
|
conversationStarters = [],
|
|
48
|
+
initialPrompt = "",
|
|
45
49
|
onchatsaved,
|
|
46
50
|
isAgentPreviewChat = false,
|
|
51
|
+
readOnly = false,
|
|
52
|
+
readOnlyReason,
|
|
47
53
|
}: Props = $props()
|
|
48
54
|
|
|
49
55
|
let API = $state(
|
|
@@ -60,6 +66,7 @@
|
|
|
60
66
|
let textareaElement = $state<HTMLTextAreaElement>()
|
|
61
67
|
let expandedTools = $state<Record<string, boolean>>({})
|
|
62
68
|
let inputValue = $state("")
|
|
69
|
+
let lastInitialPrompt = $state("")
|
|
63
70
|
let reasoningTimers = $state<Record<string, number>>({})
|
|
64
71
|
|
|
65
72
|
const getReasoningText = (message: UIMessage<AgentMessageMetadata>) =>
|
|
@@ -129,6 +136,8 @@
|
|
|
129
136
|
return () => clearInterval(interval)
|
|
130
137
|
})
|
|
131
138
|
|
|
139
|
+
const PREVIEW_CHAT_APP_ID = "agent-preview"
|
|
140
|
+
|
|
132
141
|
let resolvedChatAppId = $state<string | undefined>()
|
|
133
142
|
let resolvedConversationId = $state<string | undefined>()
|
|
134
143
|
|
|
@@ -141,6 +150,20 @@
|
|
|
141
150
|
tick().then(() => textareaElement?.focus())
|
|
142
151
|
}
|
|
143
152
|
|
|
153
|
+
$effect(() => {
|
|
154
|
+
if (!initialPrompt) {
|
|
155
|
+
lastInitialPrompt = ""
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (initialPrompt === lastInitialPrompt) {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
lastInitialPrompt = initialPrompt
|
|
164
|
+
applyConversationStarter(initialPrompt)
|
|
165
|
+
})
|
|
166
|
+
|
|
144
167
|
const chatInstance = new Chat<UIMessage<AgentMessageMetadata>>({
|
|
145
168
|
transport: new DefaultChatTransport({
|
|
146
169
|
headers: () => ({ [Header.APP_ID]: workspaceId }),
|
|
@@ -198,12 +221,20 @@
|
|
|
198
221
|
let isBusy = $derived(
|
|
199
222
|
chatInstance.status === "streaming" || chatInstance.status === "submitted"
|
|
200
223
|
)
|
|
201
|
-
let hasMessages = $derived(Boolean(
|
|
224
|
+
let hasMessages = $derived(Boolean(messages?.length))
|
|
202
225
|
let showConversationStarters = $derived(
|
|
203
226
|
!isBusy &&
|
|
204
227
|
!hasMessages &&
|
|
205
228
|
conversationStarters.length > 0 &&
|
|
206
|
-
!isAgentPreviewChat
|
|
229
|
+
!isAgentPreviewChat &&
|
|
230
|
+
!readOnly
|
|
231
|
+
)
|
|
232
|
+
let readOnlyMessage = $derived(
|
|
233
|
+
readOnlyReason === "deleted"
|
|
234
|
+
? "This agent was deleted. Select another agent to resume chatting."
|
|
235
|
+
: readOnlyReason === "offline"
|
|
236
|
+
? "This agent is no longer live. Make it live in Settings to resume chatting."
|
|
237
|
+
: "This agent is disabled. Enable it in Settings to resume chatting."
|
|
207
238
|
)
|
|
208
239
|
|
|
209
240
|
let lastChatId = $state<string | undefined>(chat?._id)
|
|
@@ -233,6 +264,15 @@
|
|
|
233
264
|
resolvedChatAppId = chat.chatAppId
|
|
234
265
|
return chat.chatAppId
|
|
235
266
|
}
|
|
267
|
+
|
|
268
|
+
if (isAgentPreviewChat) {
|
|
269
|
+
resolvedChatAppId = PREVIEW_CHAT_APP_ID
|
|
270
|
+
if (chat) {
|
|
271
|
+
chat = { ...chat, chatAppId: PREVIEW_CHAT_APP_ID }
|
|
272
|
+
}
|
|
273
|
+
return PREVIEW_CHAT_APP_ID
|
|
274
|
+
}
|
|
275
|
+
|
|
236
276
|
try {
|
|
237
277
|
const chatApp = await API.fetchChatApp(workspaceId)
|
|
238
278
|
if (chatApp?._id) {
|
|
@@ -262,6 +302,10 @@
|
|
|
262
302
|
}
|
|
263
303
|
|
|
264
304
|
const handleKeyDown = async (event: KeyboardEvent) => {
|
|
305
|
+
if (readOnly) {
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
265
309
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
266
310
|
event.preventDefault()
|
|
267
311
|
await sendMessage()
|
|
@@ -269,6 +313,10 @@
|
|
|
269
313
|
}
|
|
270
314
|
|
|
271
315
|
const sendMessage = async () => {
|
|
316
|
+
if (readOnly) {
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
272
320
|
const chatAppIdFromEnsure = await ensureChatApp()
|
|
273
321
|
|
|
274
322
|
if (!chat) {
|
|
@@ -290,7 +338,9 @@
|
|
|
290
338
|
|
|
291
339
|
resolvedChatAppId = chatAppId
|
|
292
340
|
|
|
293
|
-
if (
|
|
341
|
+
if (isAgentPreviewChat) {
|
|
342
|
+
resolvedConversationId = chat._id
|
|
343
|
+
} else if (
|
|
294
344
|
persistConversation &&
|
|
295
345
|
!chat._id &&
|
|
296
346
|
(!chat.messages || chat.messages.length === 0)
|
|
@@ -351,7 +401,9 @@
|
|
|
351
401
|
mounted = true
|
|
352
402
|
ensureChatApp()
|
|
353
403
|
tick().then(() => {
|
|
354
|
-
|
|
404
|
+
if (!readOnly) {
|
|
405
|
+
textareaElement?.focus()
|
|
406
|
+
}
|
|
355
407
|
})
|
|
356
408
|
}
|
|
357
409
|
})
|
|
@@ -389,7 +441,7 @@
|
|
|
389
441
|
{/each}
|
|
390
442
|
</div>
|
|
391
443
|
</div>
|
|
392
|
-
{:else}
|
|
444
|
+
{:else if !hasMessages}
|
|
393
445
|
<div class="empty-state">
|
|
394
446
|
<div class="empty-state-icon">
|
|
395
447
|
<Icon
|
|
@@ -570,16 +622,26 @@
|
|
|
570
622
|
{/each}
|
|
571
623
|
</div>
|
|
572
624
|
|
|
573
|
-
|
|
574
|
-
<
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
625
|
+
{#if readOnly}
|
|
626
|
+
<div class="input-wrapper">
|
|
627
|
+
<div class="read-only-notice">
|
|
628
|
+
<Body size="S" color="var(--spectrum-global-color-gray-700)">
|
|
629
|
+
{readOnlyMessage}
|
|
630
|
+
</Body>
|
|
631
|
+
</div>
|
|
632
|
+
</div>
|
|
633
|
+
{:else}
|
|
634
|
+
<div class="input-wrapper">
|
|
635
|
+
<textarea
|
|
636
|
+
bind:value={inputValue}
|
|
637
|
+
bind:this={textareaElement}
|
|
638
|
+
class="input spectrum-Textfield-input"
|
|
639
|
+
onkeydown={handleKeyDown}
|
|
640
|
+
placeholder="Ask anything"
|
|
641
|
+
disabled={isBusy}
|
|
642
|
+
></textarea>
|
|
643
|
+
</div>
|
|
644
|
+
{/if}
|
|
583
645
|
</div>
|
|
584
646
|
|
|
585
647
|
<style>
|
|
@@ -616,36 +678,45 @@
|
|
|
616
678
|
.starter-section {
|
|
617
679
|
display: flex;
|
|
618
680
|
flex-direction: column;
|
|
619
|
-
|
|
681
|
+
align-items: center;
|
|
682
|
+
gap: var(--spacing-xl);
|
|
683
|
+
margin: auto 0;
|
|
620
684
|
}
|
|
621
685
|
|
|
622
686
|
.starter-title {
|
|
623
|
-
font-size:
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
687
|
+
font-size: 14px;
|
|
688
|
+
letter-spacing: 0;
|
|
689
|
+
color: var(--spectrum-global-color-gray-700);
|
|
690
|
+
text-align: center;
|
|
627
691
|
}
|
|
628
692
|
|
|
629
693
|
.starter-grid {
|
|
630
694
|
display: grid;
|
|
631
695
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
632
|
-
gap: var(--spacing-
|
|
696
|
+
gap: var(--spacing-m);
|
|
697
|
+
width: min(520px, 100%);
|
|
698
|
+
margin: 0 auto;
|
|
633
699
|
}
|
|
634
700
|
|
|
635
701
|
.starter-card {
|
|
636
|
-
border: 1px solid var(--
|
|
702
|
+
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
637
703
|
border-radius: 12px;
|
|
638
704
|
padding: var(--spacing-m);
|
|
639
|
-
background: var(--
|
|
640
|
-
color: var(--spectrum-global-color-gray-
|
|
705
|
+
background: var(--spectrum-global-color-gray-50);
|
|
706
|
+
color: var(--spectrum-global-color-gray-800);
|
|
641
707
|
font: inherit;
|
|
642
|
-
|
|
708
|
+
font-size: 14px;
|
|
709
|
+
line-height: 1.4;
|
|
710
|
+
text-align: center;
|
|
643
711
|
cursor: pointer;
|
|
712
|
+
display: flex;
|
|
713
|
+
align-items: center;
|
|
714
|
+
justify-content: center;
|
|
644
715
|
}
|
|
645
716
|
|
|
646
717
|
.starter-card:hover {
|
|
647
|
-
border-color: var(--
|
|
648
|
-
background: var(--
|
|
718
|
+
border-color: var(--spectrum-global-color-gray-300);
|
|
719
|
+
background: var(--spectrum-global-color-gray-100);
|
|
649
720
|
}
|
|
650
721
|
|
|
651
722
|
.message {
|
|
@@ -688,6 +759,14 @@
|
|
|
688
759
|
line-height: 1.4;
|
|
689
760
|
}
|
|
690
761
|
|
|
762
|
+
.read-only-notice {
|
|
763
|
+
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
764
|
+
border-radius: 10px;
|
|
765
|
+
padding: var(--spacing-m);
|
|
766
|
+
background-color: var(--spectrum-global-color-gray-50);
|
|
767
|
+
text-align: center;
|
|
768
|
+
}
|
|
769
|
+
|
|
691
770
|
.input {
|
|
692
771
|
width: 100%;
|
|
693
772
|
height: 100px;
|