@budibase/frontend-core 3.42.0 → 3.44.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/agents.ts +26 -57
- package/src/api/ai.ts +26 -0
- package/src/api/chatLinks.ts +26 -6
- package/src/api/escalations.ts +6 -2
- package/src/api/index.ts +17 -5
- package/src/api/restTemplates.ts +74 -0
- package/src/api/types.ts +3 -2
- package/src/components/Chatbox/index.svelte +63 -323
- package/src/components/UserAvatars.svelte +1 -1
- package/src/components/grid/cells/DateCell.svelte +1 -1
- package/src/components/index.ts +0 -2
- package/src/utils/download.test.js +34 -0
- package/src/api/chatApps.ts +0 -268
- package/src/components/Chatbox/ChatConversationPanel.svelte +0 -401
- package/src/components/Chatbox/ChatNavigationPanel.svelte +0 -326
|
@@ -37,12 +37,6 @@
|
|
|
37
37
|
interface Props {
|
|
38
38
|
workspaceId: string
|
|
39
39
|
chat: ChatConversationLike
|
|
40
|
-
persistConversation?: boolean
|
|
41
|
-
conversationStarters?: { prompt: string }[]
|
|
42
|
-
initialPrompt?: string
|
|
43
|
-
onchatsaved?: (event: {
|
|
44
|
-
detail: { chatId?: string; chat: ChatConversationLike }
|
|
45
|
-
}) => void
|
|
46
40
|
// Fired when an escalation parks; the consumer polls the outcome and
|
|
47
41
|
// injects it via appendAssistantMessage.
|
|
48
42
|
onEscalationPending?: (detail: { escalationId: string }) => void
|
|
@@ -57,9 +51,7 @@
|
|
|
57
51
|
escalationId: string,
|
|
58
52
|
accepted: boolean
|
|
59
53
|
) => Promise<EscalationRespondResult | undefined>
|
|
60
|
-
|
|
61
|
-
readOnly?: boolean
|
|
62
|
-
readOnlyReason?: "disabled" | "deleted" | "offline"
|
|
54
|
+
previewRoleId?: string
|
|
63
55
|
promptHistory?: string[]
|
|
64
56
|
onpromptsubmitted?: (prompt: string) => void
|
|
65
57
|
}
|
|
@@ -67,17 +59,11 @@
|
|
|
67
59
|
let {
|
|
68
60
|
workspaceId,
|
|
69
61
|
chat = $bindable(),
|
|
70
|
-
persistConversation = true,
|
|
71
|
-
conversationStarters = [],
|
|
72
|
-
initialPrompt = "",
|
|
73
|
-
onchatsaved,
|
|
74
62
|
onEscalationPending,
|
|
75
63
|
escalationState,
|
|
76
64
|
showInlineApproval = false,
|
|
77
65
|
onResolve,
|
|
78
|
-
|
|
79
|
-
readOnly = false,
|
|
80
|
-
readOnlyReason,
|
|
66
|
+
previewRoleId,
|
|
81
67
|
promptHistory = [],
|
|
82
68
|
onpromptsubmitted,
|
|
83
69
|
}: Props = $props()
|
|
@@ -129,8 +115,7 @@
|
|
|
129
115
|
})
|
|
130
116
|
)
|
|
131
117
|
|
|
132
|
-
const createStableSessionId = () =>
|
|
133
|
-
isAgentPreviewChat ? `chat-preview:${Helpers.uuid()}` : Helpers.uuid()
|
|
118
|
+
const createStableSessionId = () => `chat-preview:${Helpers.uuid()}`
|
|
134
119
|
|
|
135
120
|
let stableSessionId = $state(createStableSessionId())
|
|
136
121
|
let chatAreaElement = $state<HTMLDivElement>()
|
|
@@ -139,7 +124,6 @@
|
|
|
139
124
|
let reasoningTextByMessageId = $state<Record<string, string>>({})
|
|
140
125
|
let inputValue = $state("")
|
|
141
126
|
let promptHistoryIndex = $state<number | undefined>()
|
|
142
|
-
let lastInitialPrompt = $state("")
|
|
143
127
|
let isPreparingResponse = $state(false)
|
|
144
128
|
const resetPendingResponse = () => {
|
|
145
129
|
isPreparingResponse = false
|
|
@@ -163,27 +147,15 @@
|
|
|
163
147
|
|
|
164
148
|
try {
|
|
165
149
|
const resolvedUrl =
|
|
166
|
-
|
|
167
|
-
chat?.chatAppId &&
|
|
168
|
-
chat?.agentId &&
|
|
169
|
-
selectedOperationId
|
|
150
|
+
chat?.agentId && selectedOperationId
|
|
170
151
|
? (
|
|
171
|
-
await API.
|
|
172
|
-
chat.chatAppId,
|
|
152
|
+
await API.fetchOperationFileUrl(
|
|
173
153
|
chat.agentId,
|
|
174
|
-
|
|
175
|
-
|
|
154
|
+
selectedOperationId,
|
|
155
|
+
source.fileId
|
|
176
156
|
)
|
|
177
157
|
).url
|
|
178
|
-
:
|
|
179
|
-
? (
|
|
180
|
-
await API.fetchOperationFileUrl(
|
|
181
|
-
chat.agentId,
|
|
182
|
-
selectedOperationId,
|
|
183
|
-
source.fileId
|
|
184
|
-
)
|
|
185
|
-
).url
|
|
186
|
-
: undefined
|
|
158
|
+
: undefined
|
|
187
159
|
if (!resolvedUrl) {
|
|
188
160
|
notifications.error("Could not resolve source file URL")
|
|
189
161
|
return
|
|
@@ -290,47 +262,21 @@
|
|
|
290
262
|
return displayName
|
|
291
263
|
}
|
|
292
264
|
|
|
293
|
-
const PREVIEW_CHAT_APP_ID = "agent-preview"
|
|
294
|
-
|
|
295
|
-
let resolvedChatAppId = $state<string | undefined>()
|
|
296
265
|
let resolvedConversationId = $state<string | undefined>()
|
|
297
266
|
|
|
298
|
-
const applyConversationStarter = async (starterPrompt: string) => {
|
|
299
|
-
if (isBusy) {
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
inputValue = starterPrompt
|
|
303
|
-
await sendMessage()
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
$effect(() => {
|
|
307
|
-
if (!initialPrompt) {
|
|
308
|
-
lastInitialPrompt = ""
|
|
309
|
-
return
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (initialPrompt === lastInitialPrompt) {
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
lastInitialPrompt = initialPrompt
|
|
317
|
-
applyConversationStarter(initialPrompt)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
267
|
const chatInstance = new Chat<UIMessage<AgentMessageMetadata>>({
|
|
321
268
|
transport: new DefaultChatTransport({
|
|
322
269
|
headers: () => ({ [Header.WORKSPACE_ID]: workspaceId }),
|
|
323
270
|
prepareSendMessagesRequest: ({ messages }) => {
|
|
324
|
-
const chatAppId = resolvedChatAppId || chat?.chatAppId
|
|
325
271
|
const conversationId = resolvedConversationId || chat?._id || "new"
|
|
272
|
+
const agentId = chat?.agentId
|
|
326
273
|
return {
|
|
327
|
-
api: `/api/
|
|
274
|
+
api: `/api/agents/${agentId}/conversations/${conversationId}/stream`,
|
|
328
275
|
body: {
|
|
329
276
|
_id: resolvedConversationId || chat?._id,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
isPreview: isAgentPreviewChat,
|
|
277
|
+
agentId,
|
|
278
|
+
isPreview: true,
|
|
279
|
+
previewRoleId,
|
|
334
280
|
sessionId: stableSessionId,
|
|
335
281
|
title: chat?.title,
|
|
336
282
|
messages,
|
|
@@ -341,31 +287,7 @@
|
|
|
341
287
|
messages: chat?.messages || [],
|
|
342
288
|
onFinish: async () => {
|
|
343
289
|
isPreparingResponse = false
|
|
344
|
-
|
|
345
|
-
if (persistConversation && !chat._id && chat.chatAppId) {
|
|
346
|
-
try {
|
|
347
|
-
const history = await API.fetchChatHistory(
|
|
348
|
-
chat.chatAppId,
|
|
349
|
-
chat.agentId
|
|
350
|
-
)
|
|
351
|
-
const msgs = chatInstance.messages
|
|
352
|
-
const lastMessageId = msgs[msgs.length - 1]?.id
|
|
353
|
-
const savedConversation =
|
|
354
|
-
history?.find(convo =>
|
|
355
|
-
convo.messages.some(message => message.id === lastMessageId)
|
|
356
|
-
) || history?.[0]
|
|
357
|
-
|
|
358
|
-
if (savedConversation) {
|
|
359
|
-
chat = { ...chat, ...savedConversation }
|
|
360
|
-
resolvedConversationId = savedConversation._id
|
|
361
|
-
}
|
|
362
|
-
} catch (historyError) {
|
|
363
|
-
console.error(historyError)
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
290
|
chat = { ...chat, messages: chatInstance.messages }
|
|
368
|
-
onchatsaved?.({ detail: { chatId: chat._id, chat } })
|
|
369
291
|
},
|
|
370
292
|
onError: error => {
|
|
371
293
|
resetPendingResponse()
|
|
@@ -448,20 +370,6 @@
|
|
|
448
370
|
)
|
|
449
371
|
let canStart = $derived(inputValue.trim().length > 0)
|
|
450
372
|
let hasMessages = $derived(messages.length > 0)
|
|
451
|
-
let showConversationStarters = $derived(
|
|
452
|
-
!isRequestPending &&
|
|
453
|
-
!hasMessages &&
|
|
454
|
-
conversationStarters.length > 0 &&
|
|
455
|
-
!isAgentPreviewChat &&
|
|
456
|
-
!readOnly
|
|
457
|
-
)
|
|
458
|
-
let readOnlyMessage = $derived(
|
|
459
|
-
readOnlyReason === "deleted"
|
|
460
|
-
? "This agent was deleted. Select another agent to resume chatting."
|
|
461
|
-
: readOnlyReason === "offline"
|
|
462
|
-
? "This agent is no longer live. Make it live in Settings to resume chatting."
|
|
463
|
-
: "This agent is disabled. Enable it in Settings to resume chatting."
|
|
464
|
-
)
|
|
465
373
|
|
|
466
374
|
let lastChatId = $state<string | undefined>(chat?._id)
|
|
467
375
|
$effect(() => {
|
|
@@ -519,70 +427,22 @@
|
|
|
519
427
|
}
|
|
520
428
|
})
|
|
521
429
|
|
|
522
|
-
const ensureChatApp = async (): Promise<string | undefined> => {
|
|
523
|
-
if (chat?.chatAppId) {
|
|
524
|
-
resolvedChatAppId = chat.chatAppId
|
|
525
|
-
return chat.chatAppId
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (isAgentPreviewChat) {
|
|
529
|
-
resolvedChatAppId = PREVIEW_CHAT_APP_ID
|
|
530
|
-
if (chat) {
|
|
531
|
-
chat = { ...chat, chatAppId: PREVIEW_CHAT_APP_ID }
|
|
532
|
-
}
|
|
533
|
-
return PREVIEW_CHAT_APP_ID
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
try {
|
|
537
|
-
const chatApp = await API.fetchChatApp(workspaceId)
|
|
538
|
-
if (chatApp?._id) {
|
|
539
|
-
const baseChat = chat || {
|
|
540
|
-
title: "",
|
|
541
|
-
messages: [],
|
|
542
|
-
chatAppId: "",
|
|
543
|
-
agentId: "",
|
|
544
|
-
}
|
|
545
|
-
const fallbackAgentId =
|
|
546
|
-
chatApp.agents?.find(agent => agent.isEnabled && agent.isDefault)
|
|
547
|
-
?.agentId || chatApp.agents?.find(agent => agent.isEnabled)?.agentId
|
|
548
|
-
chat = {
|
|
549
|
-
...baseChat,
|
|
550
|
-
chatAppId: chatApp._id,
|
|
551
|
-
...(fallbackAgentId && !baseChat.agentId
|
|
552
|
-
? { agentId: fallbackAgentId }
|
|
553
|
-
: {}),
|
|
554
|
-
}
|
|
555
|
-
resolvedChatAppId = chatApp._id
|
|
556
|
-
return chatApp._id
|
|
557
|
-
}
|
|
558
|
-
} catch (err) {
|
|
559
|
-
console.error(err)
|
|
560
|
-
}
|
|
561
|
-
return undefined
|
|
562
|
-
}
|
|
563
|
-
|
|
564
430
|
const handleKeyDown = async (event: KeyboardEvent) => {
|
|
565
|
-
|
|
431
|
+
const navigationState = navigatePromptHistory({
|
|
432
|
+
key: event.key,
|
|
433
|
+
history: promptHistory,
|
|
434
|
+
inputValue,
|
|
435
|
+
index: promptHistoryIndex,
|
|
436
|
+
})
|
|
437
|
+
if (navigationState) {
|
|
438
|
+
event.preventDefault()
|
|
439
|
+
inputValue = navigationState.inputValue
|
|
440
|
+
promptHistoryIndex = navigationState.index
|
|
441
|
+
await tick()
|
|
442
|
+
textareaElement?.setSelectionRange(inputValue.length, inputValue.length)
|
|
566
443
|
return
|
|
567
444
|
}
|
|
568
445
|
|
|
569
|
-
if (isAgentPreviewChat) {
|
|
570
|
-
const navigationState = navigatePromptHistory({
|
|
571
|
-
key: event.key,
|
|
572
|
-
history: promptHistory,
|
|
573
|
-
inputValue,
|
|
574
|
-
index: promptHistoryIndex,
|
|
575
|
-
})
|
|
576
|
-
if (navigationState) {
|
|
577
|
-
event.preventDefault()
|
|
578
|
-
inputValue = navigationState.inputValue
|
|
579
|
-
promptHistoryIndex = navigationState.index
|
|
580
|
-
await tick()
|
|
581
|
-
textareaElement?.setSelectionRange(inputValue.length, inputValue.length)
|
|
582
|
-
return
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
446
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
587
447
|
event.preventDefault()
|
|
588
448
|
await sendMessage()
|
|
@@ -590,10 +450,6 @@
|
|
|
590
450
|
}
|
|
591
451
|
|
|
592
452
|
const sendMessage = async () => {
|
|
593
|
-
if (readOnly) {
|
|
594
|
-
return
|
|
595
|
-
}
|
|
596
|
-
|
|
597
453
|
const text = inputValue.trim()
|
|
598
454
|
if (!text) {
|
|
599
455
|
return
|
|
@@ -609,52 +465,22 @@
|
|
|
609
465
|
|
|
610
466
|
isPreparingResponse = true
|
|
611
467
|
|
|
612
|
-
const chatAppIdFromEnsure = await ensureChatApp()
|
|
613
|
-
|
|
614
468
|
if (!chat) {
|
|
615
|
-
chat = {
|
|
469
|
+
chat = {
|
|
470
|
+
title: "",
|
|
471
|
+
messages: [],
|
|
472
|
+
agentId: "",
|
|
473
|
+
}
|
|
616
474
|
}
|
|
617
475
|
|
|
618
|
-
const chatAppId = chat.chatAppId || chatAppIdFromEnsure
|
|
619
476
|
const agentId = chat.agentId
|
|
620
477
|
|
|
621
|
-
if (!chatAppId) {
|
|
622
|
-
failToStartResponse("Chat app could not be created")
|
|
623
|
-
return
|
|
624
|
-
}
|
|
625
|
-
|
|
626
478
|
if (!agentId) {
|
|
627
479
|
failToStartResponse("Agent is required to start a chat")
|
|
628
480
|
return
|
|
629
481
|
}
|
|
630
482
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (isAgentPreviewChat) {
|
|
634
|
-
resolvedConversationId = chat._id
|
|
635
|
-
} else if (
|
|
636
|
-
persistConversation &&
|
|
637
|
-
!chat._id &&
|
|
638
|
-
(!chat.messages || chat.messages.length === 0)
|
|
639
|
-
) {
|
|
640
|
-
try {
|
|
641
|
-
const newChat = await API.createChatConversation(
|
|
642
|
-
{ chatAppId, agentId, title: chat.title },
|
|
643
|
-
workspaceId
|
|
644
|
-
)
|
|
645
|
-
chat = { ...chat, ...newChat, chatAppId }
|
|
646
|
-
resolvedConversationId = newChat._id
|
|
647
|
-
} catch (err: unknown) {
|
|
648
|
-
const errorMessage =
|
|
649
|
-
err instanceof Error
|
|
650
|
-
? err.message
|
|
651
|
-
: "Could not start a new chat conversation"
|
|
652
|
-
failToStartResponse(errorMessage, err)
|
|
653
|
-
return
|
|
654
|
-
}
|
|
655
|
-
} else if (chat._id) {
|
|
656
|
-
resolvedConversationId = chat._id
|
|
657
|
-
}
|
|
483
|
+
resolvedConversationId = chat._id
|
|
658
484
|
|
|
659
485
|
inputValue = ""
|
|
660
486
|
promptHistoryIndex = undefined
|
|
@@ -686,8 +512,6 @@
|
|
|
686
512
|
.map(p => p.text)
|
|
687
513
|
.join("") || "[Empty message]"
|
|
688
514
|
|
|
689
|
-
let mounted = $state(false)
|
|
690
|
-
|
|
691
515
|
$effect(() => {
|
|
692
516
|
const currentChatId = chat?._id
|
|
693
517
|
if (currentChatId) {
|
|
@@ -696,14 +520,7 @@
|
|
|
696
520
|
})
|
|
697
521
|
|
|
698
522
|
$effect(() => {
|
|
699
|
-
if (
|
|
700
|
-
mounted = true
|
|
701
|
-
ensureChatApp()
|
|
702
|
-
}
|
|
703
|
-
})
|
|
704
|
-
|
|
705
|
-
$effect(() => {
|
|
706
|
-
if (readOnly || isRequestPending) {
|
|
523
|
+
if (isRequestPending) {
|
|
707
524
|
return
|
|
708
525
|
}
|
|
709
526
|
|
|
@@ -730,22 +547,7 @@
|
|
|
730
547
|
|
|
731
548
|
<div class="chat-area" bind:this={chatAreaElement}>
|
|
732
549
|
<div class="chatbox">
|
|
733
|
-
{#if
|
|
734
|
-
<div class="starter-section">
|
|
735
|
-
<div class="starter-title">Conversation starters</div>
|
|
736
|
-
<div class="starter-grid">
|
|
737
|
-
{#each conversationStarters as starter, index (index)}
|
|
738
|
-
<button
|
|
739
|
-
type="button"
|
|
740
|
-
class="starter-card"
|
|
741
|
-
onclick={() => applyConversationStarter(starter.prompt)}
|
|
742
|
-
>
|
|
743
|
-
{starter.prompt}
|
|
744
|
-
</button>
|
|
745
|
-
{/each}
|
|
746
|
-
</div>
|
|
747
|
-
</div>
|
|
748
|
-
{:else if !hasMessages && !isRequestPending}
|
|
550
|
+
{#if !hasMessages && !isRequestPending}
|
|
749
551
|
<div class="empty-state">
|
|
750
552
|
<div class="empty-state-icon">
|
|
751
553
|
<Icon
|
|
@@ -964,48 +766,38 @@
|
|
|
964
766
|
{/if}
|
|
965
767
|
</div>
|
|
966
768
|
|
|
967
|
-
|
|
968
|
-
<div class="input-
|
|
969
|
-
<
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
769
|
+
<div class="input-wrapper">
|
|
770
|
+
<div class="input-container">
|
|
771
|
+
<textarea
|
|
772
|
+
bind:value={inputValue}
|
|
773
|
+
bind:this={textareaElement}
|
|
774
|
+
class="input spectrum-Textfield-input"
|
|
775
|
+
onkeydown={handleKeyDown}
|
|
776
|
+
oninput={() => (promptHistoryIndex = undefined)}
|
|
777
|
+
placeholder="Ask..."
|
|
778
|
+
disabled={isRequestPending}
|
|
779
|
+
></textarea>
|
|
780
|
+
<button
|
|
781
|
+
type="button"
|
|
782
|
+
class="prompt-action"
|
|
783
|
+
class:running={isRequestPending}
|
|
784
|
+
onclick={handlePromptAction}
|
|
785
|
+
aria-label={isBusy ? "Pause response" : "Start response"}
|
|
786
|
+
disabled={isPreparingResponse || (!isBusy && !canStart)}
|
|
787
|
+
>
|
|
788
|
+
{#if isBusy}
|
|
789
|
+
<Icon name="stop" size="M" weight="fill" color="#ffffff" />
|
|
790
|
+
{:else if isPreparingResponse}
|
|
791
|
+
<ProgressCircle size="S" />
|
|
792
|
+
{:else}
|
|
793
|
+
<Icon name="arrow-up" size="M" weight="bold" color="#111111" />
|
|
794
|
+
{/if}
|
|
795
|
+
</button>
|
|
974
796
|
</div>
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
<div class="input-container">
|
|
978
|
-
<textarea
|
|
979
|
-
bind:value={inputValue}
|
|
980
|
-
bind:this={textareaElement}
|
|
981
|
-
class="input spectrum-Textfield-input"
|
|
982
|
-
onkeydown={handleKeyDown}
|
|
983
|
-
oninput={() => (promptHistoryIndex = undefined)}
|
|
984
|
-
placeholder="Ask..."
|
|
985
|
-
disabled={isRequestPending}
|
|
986
|
-
></textarea>
|
|
987
|
-
<button
|
|
988
|
-
type="button"
|
|
989
|
-
class="prompt-action"
|
|
990
|
-
class:running={isRequestPending}
|
|
991
|
-
onclick={handlePromptAction}
|
|
992
|
-
aria-label={isBusy ? "Pause response" : "Start response"}
|
|
993
|
-
disabled={isPreparingResponse || (!isBusy && !canStart)}
|
|
994
|
-
>
|
|
995
|
-
{#if isBusy}
|
|
996
|
-
<Icon name="stop" size="M" weight="fill" color="#ffffff" />
|
|
997
|
-
{:else if isPreparingResponse}
|
|
998
|
-
<ProgressCircle size="S" />
|
|
999
|
-
{:else}
|
|
1000
|
-
<Icon name="arrow-up" size="M" weight="bold" color="#111111" />
|
|
1001
|
-
{/if}
|
|
1002
|
-
</button>
|
|
1003
|
-
</div>
|
|
1004
|
-
<div class="input-footer">
|
|
1005
|
-
<ContextUsage usage={lastAssistantUsage} />
|
|
1006
|
-
</div>
|
|
797
|
+
<div class="input-footer">
|
|
798
|
+
<ContextUsage usage={lastAssistantUsage} />
|
|
1007
799
|
</div>
|
|
1008
|
-
|
|
800
|
+
</div>
|
|
1009
801
|
</div>
|
|
1010
802
|
|
|
1011
803
|
<style>
|
|
@@ -1047,50 +839,6 @@
|
|
|
1047
839
|
.empty-state-icon {
|
|
1048
840
|
--size: 24px;
|
|
1049
841
|
}
|
|
1050
|
-
.starter-section {
|
|
1051
|
-
display: flex;
|
|
1052
|
-
flex-direction: column;
|
|
1053
|
-
align-items: center;
|
|
1054
|
-
gap: var(--spacing-xl);
|
|
1055
|
-
margin: auto 0;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
.starter-title {
|
|
1059
|
-
font-size: 14px;
|
|
1060
|
-
letter-spacing: 0;
|
|
1061
|
-
color: var(--spectrum-global-color-gray-700);
|
|
1062
|
-
text-align: center;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
.starter-grid {
|
|
1066
|
-
display: grid;
|
|
1067
|
-
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
1068
|
-
gap: var(--spacing-m);
|
|
1069
|
-
width: min(520px, 100%);
|
|
1070
|
-
margin: 0 auto;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
.starter-card {
|
|
1074
|
-
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
1075
|
-
border-radius: 12px;
|
|
1076
|
-
padding: var(--spacing-m);
|
|
1077
|
-
background: var(--spectrum-global-color-gray-50);
|
|
1078
|
-
color: var(--spectrum-global-color-gray-800);
|
|
1079
|
-
font: inherit;
|
|
1080
|
-
font-size: 14px;
|
|
1081
|
-
line-height: 1.4;
|
|
1082
|
-
text-align: center;
|
|
1083
|
-
cursor: pointer;
|
|
1084
|
-
display: flex;
|
|
1085
|
-
align-items: center;
|
|
1086
|
-
justify-content: center;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
.starter-card:hover {
|
|
1090
|
-
border-color: var(--spectrum-global-color-gray-300);
|
|
1091
|
-
background: var(--spectrum-global-color-gray-100);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
842
|
.message {
|
|
1095
843
|
display: flex;
|
|
1096
844
|
flex-direction: column;
|
|
@@ -1138,14 +886,6 @@
|
|
|
1138
886
|
padding: 0 4px;
|
|
1139
887
|
}
|
|
1140
888
|
|
|
1141
|
-
.read-only-notice {
|
|
1142
|
-
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
1143
|
-
border-radius: 10px;
|
|
1144
|
-
padding: var(--spacing-m);
|
|
1145
|
-
background-color: var(--spectrum-global-color-gray-50);
|
|
1146
|
-
text-align: center;
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
889
|
.input-container {
|
|
1150
890
|
position: relative;
|
|
1151
891
|
width: 100%;
|
package/src/components/index.ts
CHANGED
|
@@ -11,5 +11,3 @@ export { default as ChangePasswordModal } from "./ChangePasswordModal.svelte"
|
|
|
11
11
|
export { default as ProfileModal } from "./ProfileModal.svelte"
|
|
12
12
|
export { default as PasswordRepeatInput } from "./PasswordRepeatInput.svelte"
|
|
13
13
|
export { default as Chatbox } from "./Chatbox/index.svelte"
|
|
14
|
-
export { default as ChatNavigationPanel } from "./Chatbox/ChatNavigationPanel.svelte"
|
|
15
|
-
export { default as ChatConversationPanel } from "./Chatbox/ChatConversationPanel.svelte"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest"
|
|
2
|
+
import { downloadStream } from "./download.js"
|
|
3
|
+
|
|
4
|
+
describe("downloadStream", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.unstubAllGlobals()
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it("uses the filename from the Content-Disposition header", async () => {
|
|
10
|
+
const link = {
|
|
11
|
+
click: vi.fn(),
|
|
12
|
+
download: "",
|
|
13
|
+
href: "",
|
|
14
|
+
}
|
|
15
|
+
const createObjectURL = vi.fn(() => "blob:package")
|
|
16
|
+
const revokeObjectURL = vi.fn()
|
|
17
|
+
vi.stubGlobal("document", {
|
|
18
|
+
createElement: vi.fn(() => link),
|
|
19
|
+
})
|
|
20
|
+
vi.stubGlobal("URL", { createObjectURL, revokeObjectURL })
|
|
21
|
+
|
|
22
|
+
await downloadStream({
|
|
23
|
+
blob: vi.fn().mockResolvedValue(new Blob(["package"])),
|
|
24
|
+
headers: new Headers({
|
|
25
|
+
"Content-Disposition":
|
|
26
|
+
'attachment; filename="budibase-teams-support-agent-package.zip"',
|
|
27
|
+
}),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
expect(link.download).toBe("budibase-teams-support-agent-package.zip")
|
|
31
|
+
expect(link.click).toHaveBeenCalledOnce()
|
|
32
|
+
expect(revokeObjectURL).toHaveBeenCalledWith("blob:package")
|
|
33
|
+
})
|
|
34
|
+
})
|