@budibase/frontend-core 3.27.3 → 3.27.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.
- package/package.json +2 -2
- package/src/api/rows.ts +8 -2
- package/src/api/user.ts +7 -3
- package/src/components/Chatbox/index.svelte +108 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.27.
|
|
3
|
+
"version": "3.27.5",
|
|
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": "46316dfbbf8e41a547bc8a4977ccd819a7515b9a"
|
|
28
28
|
}
|
package/src/api/rows.ts
CHANGED
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
import { BaseAPIClient } from "./types"
|
|
12
12
|
|
|
13
13
|
export interface RowEndpoints {
|
|
14
|
-
fetchRow: (
|
|
14
|
+
fetchRow: (
|
|
15
|
+
tableId: string,
|
|
16
|
+
rowId: string,
|
|
17
|
+
suppressErrors?: boolean
|
|
18
|
+
) => Promise<FindRowResponse>
|
|
15
19
|
saveRow: (
|
|
16
20
|
row: SaveRowRequest,
|
|
17
21
|
suppressErrors?: boolean
|
|
@@ -34,10 +38,12 @@ export const buildRowEndpoints = (API: BaseAPIClient): RowEndpoints => ({
|
|
|
34
38
|
* Fetches data about a certain row in a data source.
|
|
35
39
|
* @param sourceId the ID of the table or view to fetch from
|
|
36
40
|
* @param rowId the ID of the row to fetch
|
|
41
|
+
* @param suppressErrors whether or not to suppress error notifications
|
|
37
42
|
*/
|
|
38
|
-
fetchRow: async (sourceId, rowId) => {
|
|
43
|
+
fetchRow: async (sourceId, rowId, suppressErrors = false) => {
|
|
39
44
|
return await API.get({
|
|
40
45
|
url: `/api/${sourceId}/rows/${rowId}`,
|
|
46
|
+
suppressErrors,
|
|
41
47
|
})
|
|
42
48
|
},
|
|
43
49
|
|
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: (
|
|
@@ -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
|
|
|
@@ -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>) =>
|
|
@@ -78,6 +85,9 @@
|
|
|
78
85
|
part => isToolUIPart(part) && part.state === "output-error"
|
|
79
86
|
)
|
|
80
87
|
|
|
88
|
+
const getMessageError = (message: UIMessage<AgentMessageMetadata>) =>
|
|
89
|
+
message.metadata?.error
|
|
90
|
+
|
|
81
91
|
$effect(() => {
|
|
82
92
|
const interval = setInterval(() => {
|
|
83
93
|
let updated = false
|
|
@@ -138,6 +148,20 @@
|
|
|
138
148
|
tick().then(() => textareaElement?.focus())
|
|
139
149
|
}
|
|
140
150
|
|
|
151
|
+
$effect(() => {
|
|
152
|
+
if (!initialPrompt) {
|
|
153
|
+
lastInitialPrompt = ""
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (initialPrompt === lastInitialPrompt) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lastInitialPrompt = initialPrompt
|
|
162
|
+
applyConversationStarter(initialPrompt)
|
|
163
|
+
})
|
|
164
|
+
|
|
141
165
|
const chatInstance = new Chat<UIMessage<AgentMessageMetadata>>({
|
|
142
166
|
transport: new DefaultChatTransport({
|
|
143
167
|
headers: () => ({ [Header.APP_ID]: workspaceId }),
|
|
@@ -151,6 +175,7 @@
|
|
|
151
175
|
chatAppId,
|
|
152
176
|
agentId: chat?.agentId,
|
|
153
177
|
transient: !persistConversation,
|
|
178
|
+
isPreview: isAgentPreviewChat,
|
|
154
179
|
title: chat?.title,
|
|
155
180
|
messages,
|
|
156
181
|
},
|
|
@@ -194,12 +219,20 @@
|
|
|
194
219
|
let isBusy = $derived(
|
|
195
220
|
chatInstance.status === "streaming" || chatInstance.status === "submitted"
|
|
196
221
|
)
|
|
197
|
-
let hasMessages = $derived(Boolean(
|
|
222
|
+
let hasMessages = $derived(Boolean(messages?.length))
|
|
198
223
|
let showConversationStarters = $derived(
|
|
199
224
|
!isBusy &&
|
|
200
225
|
!hasMessages &&
|
|
201
226
|
conversationStarters.length > 0 &&
|
|
202
|
-
!isAgentPreviewChat
|
|
227
|
+
!isAgentPreviewChat &&
|
|
228
|
+
!readOnly
|
|
229
|
+
)
|
|
230
|
+
let readOnlyMessage = $derived(
|
|
231
|
+
readOnlyReason === "deleted"
|
|
232
|
+
? "This agent was deleted. Select another agent to resume chatting."
|
|
233
|
+
: readOnlyReason === "offline"
|
|
234
|
+
? "This agent is no longer live. Make it live in Settings to resume chatting."
|
|
235
|
+
: "This agent is disabled. Enable it in Settings to resume chatting."
|
|
203
236
|
)
|
|
204
237
|
|
|
205
238
|
let lastChatId = $state<string | undefined>(chat?._id)
|
|
@@ -258,6 +291,10 @@
|
|
|
258
291
|
}
|
|
259
292
|
|
|
260
293
|
const handleKeyDown = async (event: KeyboardEvent) => {
|
|
294
|
+
if (readOnly) {
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
261
298
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
262
299
|
event.preventDefault()
|
|
263
300
|
await sendMessage()
|
|
@@ -265,6 +302,10 @@
|
|
|
265
302
|
}
|
|
266
303
|
|
|
267
304
|
const sendMessage = async () => {
|
|
305
|
+
if (readOnly) {
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
268
309
|
const chatAppIdFromEnsure = await ensureChatApp()
|
|
269
310
|
|
|
270
311
|
if (!chat) {
|
|
@@ -347,7 +388,9 @@
|
|
|
347
388
|
mounted = true
|
|
348
389
|
ensureChatApp()
|
|
349
390
|
tick().then(() => {
|
|
350
|
-
|
|
391
|
+
if (!readOnly) {
|
|
392
|
+
textareaElement?.focus()
|
|
393
|
+
}
|
|
351
394
|
})
|
|
352
395
|
}
|
|
353
396
|
})
|
|
@@ -385,7 +428,7 @@
|
|
|
385
428
|
{/each}
|
|
386
429
|
</div>
|
|
387
430
|
</div>
|
|
388
|
-
{:else}
|
|
431
|
+
{:else if !hasMessages}
|
|
389
432
|
<div class="empty-state">
|
|
390
433
|
<div class="empty-state-icon">
|
|
391
434
|
<Icon
|
|
@@ -409,9 +452,13 @@
|
|
|
409
452
|
{@const reasoningText = getReasoningText(message)}
|
|
410
453
|
{@const reasoningId = `${message.id}-reasoning`}
|
|
411
454
|
{@const toolError = hasToolError(message)}
|
|
455
|
+
{@const messageError = getMessageError(message)}
|
|
412
456
|
{@const reasoningStreaming = isReasoningStreaming(message)}
|
|
413
457
|
{@const isThinking =
|
|
414
|
-
reasoningStreaming &&
|
|
458
|
+
reasoningStreaming &&
|
|
459
|
+
!toolError &&
|
|
460
|
+
!messageError &&
|
|
461
|
+
!message.metadata?.completedAt}
|
|
415
462
|
<div class="message assistant">
|
|
416
463
|
{#if reasoningText}
|
|
417
464
|
<div class="reasoning-part">
|
|
@@ -487,7 +534,7 @@
|
|
|
487
534
|
<div class="tool-name-wrapper">
|
|
488
535
|
<span class="tool-name">{getToolName(part)}</span>
|
|
489
536
|
</div>
|
|
490
|
-
{#if isRunning || isError}
|
|
537
|
+
{#if isRunning || isError || isSuccess}
|
|
491
538
|
<span class="tool-status">
|
|
492
539
|
{#if isRunning}
|
|
493
540
|
<ProgressCircle size="S" />
|
|
@@ -497,6 +544,12 @@
|
|
|
497
544
|
size="S"
|
|
498
545
|
color="var(--spectrum-global-color-red-600)"
|
|
499
546
|
/>
|
|
547
|
+
{:else if isSuccess}
|
|
548
|
+
<Icon
|
|
549
|
+
name="check"
|
|
550
|
+
size="S"
|
|
551
|
+
color="var(--spectrum-global-color-green-600)"
|
|
552
|
+
/>
|
|
500
553
|
{/if}
|
|
501
554
|
</span>
|
|
502
555
|
{/if}
|
|
@@ -556,16 +609,26 @@
|
|
|
556
609
|
{/each}
|
|
557
610
|
</div>
|
|
558
611
|
|
|
559
|
-
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
612
|
+
{#if readOnly}
|
|
613
|
+
<div class="input-wrapper">
|
|
614
|
+
<div class="read-only-notice">
|
|
615
|
+
<Body size="S" color="var(--spectrum-global-color-gray-700)">
|
|
616
|
+
{readOnlyMessage}
|
|
617
|
+
</Body>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
{:else}
|
|
621
|
+
<div class="input-wrapper">
|
|
622
|
+
<textarea
|
|
623
|
+
bind:value={inputValue}
|
|
624
|
+
bind:this={textareaElement}
|
|
625
|
+
class="input spectrum-Textfield-input"
|
|
626
|
+
onkeydown={handleKeyDown}
|
|
627
|
+
placeholder="Ask anything"
|
|
628
|
+
disabled={isBusy}
|
|
629
|
+
></textarea>
|
|
630
|
+
</div>
|
|
631
|
+
{/if}
|
|
569
632
|
</div>
|
|
570
633
|
|
|
571
634
|
<style>
|
|
@@ -602,36 +665,45 @@
|
|
|
602
665
|
.starter-section {
|
|
603
666
|
display: flex;
|
|
604
667
|
flex-direction: column;
|
|
605
|
-
|
|
668
|
+
align-items: center;
|
|
669
|
+
gap: var(--spacing-xl);
|
|
670
|
+
margin: auto 0;
|
|
606
671
|
}
|
|
607
672
|
|
|
608
673
|
.starter-title {
|
|
609
|
-
font-size:
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
674
|
+
font-size: 14px;
|
|
675
|
+
letter-spacing: 0;
|
|
676
|
+
color: var(--spectrum-global-color-gray-700);
|
|
677
|
+
text-align: center;
|
|
613
678
|
}
|
|
614
679
|
|
|
615
680
|
.starter-grid {
|
|
616
681
|
display: grid;
|
|
617
682
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
618
|
-
gap: var(--spacing-
|
|
683
|
+
gap: var(--spacing-m);
|
|
684
|
+
width: min(520px, 100%);
|
|
685
|
+
margin: 0 auto;
|
|
619
686
|
}
|
|
620
687
|
|
|
621
688
|
.starter-card {
|
|
622
|
-
border: 1px solid var(--
|
|
689
|
+
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
623
690
|
border-radius: 12px;
|
|
624
691
|
padding: var(--spacing-m);
|
|
625
|
-
background: var(--
|
|
626
|
-
color: var(--spectrum-global-color-gray-
|
|
692
|
+
background: var(--spectrum-global-color-gray-50);
|
|
693
|
+
color: var(--spectrum-global-color-gray-800);
|
|
627
694
|
font: inherit;
|
|
628
|
-
|
|
695
|
+
font-size: 14px;
|
|
696
|
+
line-height: 1.4;
|
|
697
|
+
text-align: center;
|
|
629
698
|
cursor: pointer;
|
|
699
|
+
display: flex;
|
|
700
|
+
align-items: center;
|
|
701
|
+
justify-content: center;
|
|
630
702
|
}
|
|
631
703
|
|
|
632
704
|
.starter-card:hover {
|
|
633
|
-
border-color: var(--
|
|
634
|
-
background: var(--
|
|
705
|
+
border-color: var(--spectrum-global-color-gray-300);
|
|
706
|
+
background: var(--spectrum-global-color-gray-100);
|
|
635
707
|
}
|
|
636
708
|
|
|
637
709
|
.message {
|
|
@@ -674,6 +746,14 @@
|
|
|
674
746
|
line-height: 1.4;
|
|
675
747
|
}
|
|
676
748
|
|
|
749
|
+
.read-only-notice {
|
|
750
|
+
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
751
|
+
border-radius: 10px;
|
|
752
|
+
padding: var(--spacing-m);
|
|
753
|
+
background-color: var(--spectrum-global-color-gray-50);
|
|
754
|
+
text-align: center;
|
|
755
|
+
}
|
|
756
|
+
|
|
677
757
|
.input {
|
|
678
758
|
width: 100%;
|
|
679
759
|
height: 100px;
|