@budibase/frontend-core 3.27.4 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "3.27.4",
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": "d0d77e2d7618885095e00c73cefcfefd8ad54877"
27
+ "gitHead": "46316dfbbf8e41a547bc8a4977ccd819a7515b9a"
28
28
  }
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: (code: string) => Promise<CheckInviteResponse>
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>) =>
@@ -141,6 +148,20 @@
141
148
  tick().then(() => textareaElement?.focus())
142
149
  }
143
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
+
144
165
  const chatInstance = new Chat<UIMessage<AgentMessageMetadata>>({
145
166
  transport: new DefaultChatTransport({
146
167
  headers: () => ({ [Header.APP_ID]: workspaceId }),
@@ -198,12 +219,20 @@
198
219
  let isBusy = $derived(
199
220
  chatInstance.status === "streaming" || chatInstance.status === "submitted"
200
221
  )
201
- let hasMessages = $derived(Boolean(chat?.messages?.length))
222
+ let hasMessages = $derived(Boolean(messages?.length))
202
223
  let showConversationStarters = $derived(
203
224
  !isBusy &&
204
225
  !hasMessages &&
205
226
  conversationStarters.length > 0 &&
206
- !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."
207
236
  )
208
237
 
209
238
  let lastChatId = $state<string | undefined>(chat?._id)
@@ -262,6 +291,10 @@
262
291
  }
263
292
 
264
293
  const handleKeyDown = async (event: KeyboardEvent) => {
294
+ if (readOnly) {
295
+ return
296
+ }
297
+
265
298
  if (event.key === "Enter" && !event.shiftKey) {
266
299
  event.preventDefault()
267
300
  await sendMessage()
@@ -269,6 +302,10 @@
269
302
  }
270
303
 
271
304
  const sendMessage = async () => {
305
+ if (readOnly) {
306
+ return
307
+ }
308
+
272
309
  const chatAppIdFromEnsure = await ensureChatApp()
273
310
 
274
311
  if (!chat) {
@@ -351,7 +388,9 @@
351
388
  mounted = true
352
389
  ensureChatApp()
353
390
  tick().then(() => {
354
- textareaElement?.focus()
391
+ if (!readOnly) {
392
+ textareaElement?.focus()
393
+ }
355
394
  })
356
395
  }
357
396
  })
@@ -389,7 +428,7 @@
389
428
  {/each}
390
429
  </div>
391
430
  </div>
392
- {:else}
431
+ {:else if !hasMessages}
393
432
  <div class="empty-state">
394
433
  <div class="empty-state-icon">
395
434
  <Icon
@@ -570,16 +609,26 @@
570
609
  {/each}
571
610
  </div>
572
611
 
573
- <div class="input-wrapper">
574
- <textarea
575
- bind:value={inputValue}
576
- bind:this={textareaElement}
577
- class="input spectrum-Textfield-input"
578
- onkeydown={handleKeyDown}
579
- placeholder="Ask anything"
580
- disabled={isBusy}
581
- ></textarea>
582
- </div>
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}
583
632
  </div>
584
633
 
585
634
  <style>
@@ -616,36 +665,45 @@
616
665
  .starter-section {
617
666
  display: flex;
618
667
  flex-direction: column;
619
- gap: var(--spacing-s);
668
+ align-items: center;
669
+ gap: var(--spacing-xl);
670
+ margin: auto 0;
620
671
  }
621
672
 
622
673
  .starter-title {
623
- font-size: 12px;
624
- text-transform: uppercase;
625
- letter-spacing: 0.08em;
626
- color: var(--spectrum-global-color-gray-600);
674
+ font-size: 14px;
675
+ letter-spacing: 0;
676
+ color: var(--spectrum-global-color-gray-700);
677
+ text-align: center;
627
678
  }
628
679
 
629
680
  .starter-grid {
630
681
  display: grid;
631
682
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
632
- gap: var(--spacing-s);
683
+ gap: var(--spacing-m);
684
+ width: min(520px, 100%);
685
+ margin: 0 auto;
633
686
  }
634
687
 
635
688
  .starter-card {
636
- border: 1px solid var(--grey-3);
689
+ border: 1px solid var(--spectrum-global-color-gray-200);
637
690
  border-radius: 12px;
638
691
  padding: var(--spacing-m);
639
- background: var(--grey-2);
640
- color: var(--spectrum-global-color-gray-900);
692
+ background: var(--spectrum-global-color-gray-50);
693
+ color: var(--spectrum-global-color-gray-800);
641
694
  font: inherit;
642
- text-align: left;
695
+ font-size: 14px;
696
+ line-height: 1.4;
697
+ text-align: center;
643
698
  cursor: pointer;
699
+ display: flex;
700
+ align-items: center;
701
+ justify-content: center;
644
702
  }
645
703
 
646
704
  .starter-card:hover {
647
- border-color: var(--grey-4);
648
- background: var(--grey-1);
705
+ border-color: var(--spectrum-global-color-gray-300);
706
+ background: var(--spectrum-global-color-gray-100);
649
707
  }
650
708
 
651
709
  .message {
@@ -688,6 +746,14 @@
688
746
  line-height: 1.4;
689
747
  }
690
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
+
691
757
  .input {
692
758
  width: 100%;
693
759
  height: 100px;