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