@budibase/frontend-core 3.29.0 → 3.30.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.30.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": "58456cc695d2c71a354bcf5c064943b247ad4969"
|
|
28
28
|
}
|
package/src/api/chatApps.ts
CHANGED
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
ChatConversation,
|
|
4
4
|
ChatConversationRequest,
|
|
5
5
|
CreateChatConversationRequest,
|
|
6
|
+
FetchChatAppAgentsResponse,
|
|
6
7
|
ChatApp,
|
|
7
8
|
ChatAppAgent,
|
|
8
9
|
FetchAgentHistoryResponse,
|
|
10
|
+
FetchPublishedChatAppsResponse,
|
|
9
11
|
UpdateChatAppRequest,
|
|
10
12
|
AgentMessageMetadata,
|
|
11
13
|
} from "@budibase/types"
|
|
@@ -27,6 +29,7 @@ export interface ChatAppEndpoints {
|
|
|
27
29
|
chatAppId: string,
|
|
28
30
|
chatConversationId: string
|
|
29
31
|
) => Promise<ChatConversation>
|
|
32
|
+
fetchChatAppAgents: (chatAppId: string) => Promise<FetchChatAppAgentsResponse>
|
|
30
33
|
fetchChatHistory: (chatAppId: string) => Promise<FetchAgentHistoryResponse>
|
|
31
34
|
fetchChatApp: (workspaceId?: string) => Promise<ChatApp | null>
|
|
32
35
|
setChatAppAgent: (chatAppId: string, agentId: string) => Promise<ChatAppAgent>
|
|
@@ -35,6 +38,9 @@ export interface ChatAppEndpoints {
|
|
|
35
38
|
workspaceId?: string
|
|
36
39
|
) => Promise<ChatConversation>
|
|
37
40
|
updateChatApp: (chatApp: UpdateChatAppRequest) => Promise<ChatApp>
|
|
41
|
+
getPublishedChatApps: () => Promise<
|
|
42
|
+
FetchPublishedChatAppsResponse["chatApps"]
|
|
43
|
+
>
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
const throwOnErrorChunk = () =>
|
|
@@ -112,6 +118,12 @@ export const buildChatAppEndpoints = (
|
|
|
112
118
|
})
|
|
113
119
|
},
|
|
114
120
|
|
|
121
|
+
fetchChatAppAgents: async (chatAppId: string) => {
|
|
122
|
+
return await API.get({
|
|
123
|
+
url: `/api/chatapps/${chatAppId}/agents`,
|
|
124
|
+
})
|
|
125
|
+
},
|
|
126
|
+
|
|
115
127
|
fetchChatHistory: async (chatAppId: string) => {
|
|
116
128
|
return await API.get({
|
|
117
129
|
url: `/api/chatapps/${chatAppId}/conversations`,
|
|
@@ -189,4 +201,11 @@ export const buildChatAppEndpoints = (
|
|
|
189
201
|
body: chatApp as any,
|
|
190
202
|
})
|
|
191
203
|
},
|
|
204
|
+
|
|
205
|
+
getPublishedChatApps: async () => {
|
|
206
|
+
const response = await API.get<FetchPublishedChatAppsResponse>({
|
|
207
|
+
url: "/api/client/chatapps",
|
|
208
|
+
})
|
|
209
|
+
return response.chatApps
|
|
210
|
+
},
|
|
192
211
|
})
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createEventDispatcher, onMount } from "svelte"
|
|
3
|
+
import { Body, Button, Icon, ProgressCircle } from "@budibase/bbui"
|
|
4
|
+
import type { ChatConversation, DraftChatConversation } from "@budibase/types"
|
|
5
|
+
import Chatbox from "./index.svelte"
|
|
6
|
+
|
|
7
|
+
type ChatConversationLike = ChatConversation | DraftChatConversation
|
|
8
|
+
|
|
9
|
+
type EnabledAgentListItem = {
|
|
10
|
+
agentId: string
|
|
11
|
+
name?: string
|
|
12
|
+
icon?: string
|
|
13
|
+
iconColor?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export let selectedAgentId: string | null = null
|
|
17
|
+
export let selectedAgentName: string = ""
|
|
18
|
+
export let enabledAgentList: EnabledAgentListItem[] = []
|
|
19
|
+
export let conversationStarters: { prompt: string }[] = []
|
|
20
|
+
export let isAgentKnown: boolean = true
|
|
21
|
+
export let isAgentLive: boolean = true
|
|
22
|
+
|
|
23
|
+
export let chat: ChatConversationLike
|
|
24
|
+
export let loading: boolean = false
|
|
25
|
+
export let deletingChat: boolean = false
|
|
26
|
+
export let workspaceId: string
|
|
27
|
+
export let initialPrompt: string = ""
|
|
28
|
+
export let userName: string = ""
|
|
29
|
+
|
|
30
|
+
const dispatch = createEventDispatcher<{
|
|
31
|
+
chatSaved: { chatId?: string; chat: ChatConversationLike }
|
|
32
|
+
deleteChat: undefined
|
|
33
|
+
agentSelected: { agentId: string }
|
|
34
|
+
startChat: { agentId: string; prompt: string }
|
|
35
|
+
}>()
|
|
36
|
+
|
|
37
|
+
const hasChatId = (value: ChatConversationLike) =>
|
|
38
|
+
value && "_id" in value && Boolean(value._id)
|
|
39
|
+
|
|
40
|
+
const buildGreeting = (name: string) => {
|
|
41
|
+
const currentDate = new Date()
|
|
42
|
+
const suffix = name ? `, ${name}` : ""
|
|
43
|
+
|
|
44
|
+
if (currentDate.getDay() === 1) {
|
|
45
|
+
return `Happy Monday${suffix}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isMorning = currentDate.getHours() < 12
|
|
49
|
+
return `${isMorning ? "Good Morning" : "Good Afternoon"}${suffix}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let readOnlyReason: "disabled" | "deleted" | "offline" | undefined
|
|
53
|
+
|
|
54
|
+
let draftPrompt = ""
|
|
55
|
+
let draftPromptInput: HTMLInputElement | null = null
|
|
56
|
+
|
|
57
|
+
$: greetingText = buildGreeting(userName)
|
|
58
|
+
|
|
59
|
+
$: visibleAgentList = enabledAgentList.slice(0, 3)
|
|
60
|
+
$: hasEnabledAgents = enabledAgentList.length > 0
|
|
61
|
+
|
|
62
|
+
const getAgentStatus = (
|
|
63
|
+
agentId: string | null,
|
|
64
|
+
agents: EnabledAgentListItem[],
|
|
65
|
+
agentKnown: boolean,
|
|
66
|
+
agentLive: boolean
|
|
67
|
+
): {
|
|
68
|
+
isAgentEnabled: boolean
|
|
69
|
+
readOnlyReason: "disabled" | "deleted" | "offline" | undefined
|
|
70
|
+
} => {
|
|
71
|
+
if (!agentId) {
|
|
72
|
+
return { isAgentEnabled: false, readOnlyReason: undefined }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!agentKnown) {
|
|
76
|
+
return { isAgentEnabled: false, readOnlyReason: "deleted" }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!agentLive) {
|
|
80
|
+
return { isAgentEnabled: false, readOnlyReason: "offline" }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const isAgentEnabled = agents.some(agent => agent.agentId === agentId)
|
|
84
|
+
return {
|
|
85
|
+
isAgentEnabled,
|
|
86
|
+
readOnlyReason: isAgentEnabled ? undefined : "disabled",
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
$: ({ readOnlyReason } = getAgentStatus(
|
|
91
|
+
selectedAgentId,
|
|
92
|
+
enabledAgentList,
|
|
93
|
+
isAgentKnown,
|
|
94
|
+
isAgentLive
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
const deleteChat = () => {
|
|
98
|
+
dispatch("deleteChat")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const selectAgent = (agentId: string) => {
|
|
102
|
+
dispatch("agentSelected", { agentId })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const startChat = () => {
|
|
106
|
+
const prompt = draftPrompt.trim()
|
|
107
|
+
if (!prompt) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const agentId = enabledAgentList[0]?.agentId
|
|
112
|
+
if (!agentId) {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
dispatch("startChat", { agentId, prompt })
|
|
117
|
+
draftPrompt = ""
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const handlePromptKeyDown = (event: KeyboardEvent) => {
|
|
121
|
+
if (event.key !== "Enter") {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
event.preventDefault()
|
|
126
|
+
startChat()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
onMount(() => {
|
|
130
|
+
draftPromptInput?.focus()
|
|
131
|
+
})
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<div class="chat-wrapper">
|
|
135
|
+
{#if selectedAgentId}
|
|
136
|
+
<div class="chat-header">
|
|
137
|
+
<div class="chat-header-agent">
|
|
138
|
+
<Body size="S">
|
|
139
|
+
{selectedAgentName || "Unknown agent"}
|
|
140
|
+
</Body>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{#if hasChatId(chat)}
|
|
144
|
+
<Button
|
|
145
|
+
quiet
|
|
146
|
+
warning
|
|
147
|
+
disabled={deletingChat || loading}
|
|
148
|
+
on:click={deleteChat}
|
|
149
|
+
>
|
|
150
|
+
<span class="delete-button-content">
|
|
151
|
+
{#if deletingChat}
|
|
152
|
+
<ProgressCircle size="S" />
|
|
153
|
+
Deleting...
|
|
154
|
+
{:else}
|
|
155
|
+
<Icon name="trash" size="S" />
|
|
156
|
+
Delete chat
|
|
157
|
+
{/if}
|
|
158
|
+
</span>
|
|
159
|
+
</Button>
|
|
160
|
+
{/if}
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<Chatbox
|
|
164
|
+
bind:chat
|
|
165
|
+
{workspaceId}
|
|
166
|
+
{conversationStarters}
|
|
167
|
+
{initialPrompt}
|
|
168
|
+
readOnly={Boolean(readOnlyReason)}
|
|
169
|
+
{readOnlyReason}
|
|
170
|
+
onchatsaved={event => dispatch("chatSaved", event.detail)}
|
|
171
|
+
/>
|
|
172
|
+
{:else}
|
|
173
|
+
<div class="chat-empty">
|
|
174
|
+
<div class="chat-empty-greeting">
|
|
175
|
+
<Body size="XL" weight="600" serif>
|
|
176
|
+
{greetingText}
|
|
177
|
+
</Body>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="chat-empty-input" role="presentation">
|
|
180
|
+
<input
|
|
181
|
+
class="chat-empty-input-field"
|
|
182
|
+
type="text"
|
|
183
|
+
placeholder="How can I help you today?"
|
|
184
|
+
bind:this={draftPromptInput}
|
|
185
|
+
bind:value={draftPrompt}
|
|
186
|
+
on:keydown={handlePromptKeyDown}
|
|
187
|
+
disabled={!hasEnabledAgents}
|
|
188
|
+
/>
|
|
189
|
+
<button
|
|
190
|
+
class="chat-empty-input-action"
|
|
191
|
+
type="button"
|
|
192
|
+
on:click={startChat}
|
|
193
|
+
disabled={!hasEnabledAgents}
|
|
194
|
+
aria-label="Start chat"
|
|
195
|
+
>
|
|
196
|
+
<Icon name="arrow-up" size="S" />
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="chat-empty-grid">
|
|
200
|
+
{#if visibleAgentList.length}
|
|
201
|
+
{#each visibleAgentList as agent (agent.agentId)}
|
|
202
|
+
<button
|
|
203
|
+
class="chat-empty-card"
|
|
204
|
+
class:chat-empty-card-single={visibleAgentList.length === 1}
|
|
205
|
+
on:click={() => selectAgent(agent.agentId)}
|
|
206
|
+
style={`--agent-icon-color:${
|
|
207
|
+
agent.iconColor ||
|
|
208
|
+
"var(--spectrum-semantic-cta-color-background-default)"
|
|
209
|
+
};`}
|
|
210
|
+
>
|
|
211
|
+
<div class="chat-empty-card-head">
|
|
212
|
+
<div class="chat-empty-card-icon">
|
|
213
|
+
<Icon
|
|
214
|
+
name={agent.icon || "SideKick"}
|
|
215
|
+
size="S"
|
|
216
|
+
color="var(--agent-icon-color)"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
<Body size="S" weight="500">
|
|
220
|
+
{agent.name || "Unknown agent"}
|
|
221
|
+
</Body>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="chat-empty-card-subtitle">
|
|
224
|
+
<Body size="XS" color="var(--spectrum-global-color-gray-600)">
|
|
225
|
+
Start a chat with this agent.
|
|
226
|
+
</Body>
|
|
227
|
+
</div>
|
|
228
|
+
</button>
|
|
229
|
+
{/each}
|
|
230
|
+
{:else}
|
|
231
|
+
<Body size="S" color="var(--spectrum-global-color-gray-500)">
|
|
232
|
+
No enabled agents
|
|
233
|
+
</Body>
|
|
234
|
+
{/if}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
{/if}
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<style>
|
|
241
|
+
.chat-wrapper {
|
|
242
|
+
flex: 1 1 auto;
|
|
243
|
+
display: flex;
|
|
244
|
+
flex-direction: column;
|
|
245
|
+
padding: 0 32px 32px 32px;
|
|
246
|
+
box-sizing: border-box;
|
|
247
|
+
min-width: 0;
|
|
248
|
+
min-height: 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.chat-header {
|
|
252
|
+
width: 100%;
|
|
253
|
+
padding: var(--spacing-l) 0 var(--spacing-l);
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: space-between;
|
|
257
|
+
gap: var(--spacing-m);
|
|
258
|
+
border-bottom: var(--border-dark);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.chat-header-agent {
|
|
262
|
+
display: flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.chat-header-agent :global(p) {
|
|
267
|
+
font-size: 14px;
|
|
268
|
+
line-height: 17px;
|
|
269
|
+
letter-spacing: 0;
|
|
270
|
+
font-weight: 400;
|
|
271
|
+
color: var(--spectrum-alias-text-color);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.delete-button-content {
|
|
275
|
+
display: flex;
|
|
276
|
+
align-items: center;
|
|
277
|
+
gap: var(--spacing-xs);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.chat-empty {
|
|
281
|
+
flex: 1 1 auto;
|
|
282
|
+
display: flex;
|
|
283
|
+
flex-direction: column;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 32px;
|
|
287
|
+
padding: var(--spacing-xxl);
|
|
288
|
+
text-align: center;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.chat-empty-greeting :global(p) {
|
|
292
|
+
color: var(--spectrum-alias-text-color);
|
|
293
|
+
font-size: 28px;
|
|
294
|
+
line-height: 34px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.chat-empty-input {
|
|
298
|
+
display: flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
gap: var(--spacing-m);
|
|
301
|
+
width: 600px;
|
|
302
|
+
padding: 10px;
|
|
303
|
+
padding-left: 20px;
|
|
304
|
+
border-radius: 999px;
|
|
305
|
+
background: var(--spectrum-alias-background-color-secondary);
|
|
306
|
+
color: var(--spectrum-alias-text-color);
|
|
307
|
+
border: 1px solid var(--spectrum-alias-border-color);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.chat-empty-input-field {
|
|
311
|
+
flex: 1;
|
|
312
|
+
font-size: 16px;
|
|
313
|
+
color: var(--spectrum-alias-text-color);
|
|
314
|
+
background: transparent;
|
|
315
|
+
border: none;
|
|
316
|
+
outline: none;
|
|
317
|
+
font: inherit;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.chat-empty-input-field::placeholder {
|
|
321
|
+
color: var(--spectrum-alias-text-color-disabled);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.chat-empty-input-action {
|
|
325
|
+
width: 32px;
|
|
326
|
+
height: 32px;
|
|
327
|
+
border-radius: 999px;
|
|
328
|
+
background: var(--spectrum-semantic-cta-color-background-default);
|
|
329
|
+
color: var(--spectrum-global-color-gray-50);
|
|
330
|
+
display: inline-flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
justify-content: center;
|
|
333
|
+
border: none;
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.chat-empty-input-action:disabled,
|
|
338
|
+
.chat-empty-input-field:disabled {
|
|
339
|
+
opacity: 0.5;
|
|
340
|
+
cursor: not-allowed;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.chat-empty-input-action:hover {
|
|
344
|
+
background: var(--spectrum-semantic-cta-color-background-hover);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.chat-empty-grid {
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-direction: row;
|
|
350
|
+
gap: 16px;
|
|
351
|
+
width: min(720px, 100%);
|
|
352
|
+
align-items: center;
|
|
353
|
+
justify-content: center;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.chat-empty-card {
|
|
357
|
+
border: 1px solid var(--spectrum-alias-border-color);
|
|
358
|
+
width: 240px;
|
|
359
|
+
border-radius: 16px;
|
|
360
|
+
padding: 0;
|
|
361
|
+
background: var(--spectrum-alias-background-color-primary);
|
|
362
|
+
color: var(--spectrum-alias-text-color);
|
|
363
|
+
font: inherit;
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
text-align: left;
|
|
366
|
+
overflow: hidden;
|
|
367
|
+
transform: translateY(var(--card-offset, 0px))
|
|
368
|
+
rotate(var(--card-rotation, 0deg));
|
|
369
|
+
transition:
|
|
370
|
+
border-color 150ms ease,
|
|
371
|
+
transform 150ms ease;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.chat-empty-card:hover {
|
|
375
|
+
border-color: var(--spectrum-alias-border-color-hover);
|
|
376
|
+
transform: translateY(calc(var(--card-offset, 0px) - 3px))
|
|
377
|
+
rotate(var(--card-rotation, 0deg));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.chat-empty-card:first-child {
|
|
381
|
+
--card-rotation: -6deg;
|
|
382
|
+
--card-offset: 12px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.chat-empty-card:last-child {
|
|
386
|
+
--card-rotation: 6deg;
|
|
387
|
+
--card-offset: 12px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.chat-empty-card.chat-empty-card-single {
|
|
391
|
+
--card-rotation: 0deg;
|
|
392
|
+
--card-offset: 0px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.chat-empty-card-head {
|
|
396
|
+
display: flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
gap: var(--spacing-s);
|
|
399
|
+
padding: var(--spacing-m);
|
|
400
|
+
background-color: var(--spectrum-alias-background-color-secondary);
|
|
401
|
+
color: var(--spectrum-alias-text-color);
|
|
402
|
+
border-bottom: 1px solid var(--spectrum-alias-border-color);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.chat-empty-card-icon {
|
|
406
|
+
width: 28px;
|
|
407
|
+
height: 28px;
|
|
408
|
+
border-radius: 8px;
|
|
409
|
+
display: inline-flex;
|
|
410
|
+
align-items: center;
|
|
411
|
+
justify-content: center;
|
|
412
|
+
background: transparent;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.chat-empty-card-subtitle {
|
|
416
|
+
padding: var(--spacing-m);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
@media (max-width: 1000px) {
|
|
420
|
+
.chat-wrapper {
|
|
421
|
+
padding: 0 var(--spacing-l) var(--spacing-l);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.chat-empty-input {
|
|
425
|
+
width: min(600px, 100%);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.chat-empty-grid {
|
|
429
|
+
flex-direction: column;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.chat-empty-card {
|
|
433
|
+
width: min(360px, 100%);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.chat-empty-card:first-child,
|
|
437
|
+
.chat-empty-card:last-child {
|
|
438
|
+
--card-rotation: 0deg;
|
|
439
|
+
--card-offset: 0px;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
</style>
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Body, Icon } from "@budibase/bbui"
|
|
3
|
+
import { createEventDispatcher } from "svelte"
|
|
4
|
+
|
|
5
|
+
type EnabledAgentListItem = {
|
|
6
|
+
agentId: string
|
|
7
|
+
name?: string
|
|
8
|
+
isDefault?: boolean
|
|
9
|
+
icon?: string
|
|
10
|
+
iconColor?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ConversationListItem = {
|
|
14
|
+
_id?: string
|
|
15
|
+
title?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export let enabledAgentList: EnabledAgentListItem[] = []
|
|
19
|
+
export let conversationHistory: ConversationListItem[] = []
|
|
20
|
+
export let selectedConversationId: string | undefined
|
|
21
|
+
|
|
22
|
+
$: defaultAgent =
|
|
23
|
+
enabledAgentList.find(agent => agent.isDefault) || enabledAgentList[0]
|
|
24
|
+
|
|
25
|
+
const dispatch = createEventDispatcher<{
|
|
26
|
+
agentSelected: { agentId: string }
|
|
27
|
+
conversationSelected: { conversationId: string }
|
|
28
|
+
}>()
|
|
29
|
+
|
|
30
|
+
const selectAgent = (agentId: string) => {
|
|
31
|
+
dispatch("agentSelected", { agentId })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const selectConversation = (conversationId: string) => {
|
|
35
|
+
dispatch("conversationSelected", { conversationId })
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div class="chat-nav-shell">
|
|
40
|
+
<div class="chat-nav-content">
|
|
41
|
+
{#if defaultAgent?.agentId}
|
|
42
|
+
<div class="list-section">
|
|
43
|
+
<button
|
|
44
|
+
class="new-chat"
|
|
45
|
+
on:click={() => selectAgent(defaultAgent.agentId)}
|
|
46
|
+
>
|
|
47
|
+
<span class="new-chat-icon">
|
|
48
|
+
<Icon name="plus" size="S" />
|
|
49
|
+
</span>
|
|
50
|
+
<span class="new-chat-label">New chat</span>
|
|
51
|
+
</button>
|
|
52
|
+
</div>
|
|
53
|
+
{/if}
|
|
54
|
+
|
|
55
|
+
<div class="list-section">
|
|
56
|
+
<div class="list-title">Agents</div>
|
|
57
|
+
{#if enabledAgentList.length}
|
|
58
|
+
{#each enabledAgentList as agent (agent.agentId)}
|
|
59
|
+
<button
|
|
60
|
+
class="list-item list-item-button"
|
|
61
|
+
on:click={() => selectAgent(agent.agentId)}
|
|
62
|
+
>
|
|
63
|
+
<span class="list-item-icon">
|
|
64
|
+
<Icon name={agent.icon || "robot"} size="S" />
|
|
65
|
+
</span>
|
|
66
|
+
{agent.name}
|
|
67
|
+
</button>
|
|
68
|
+
{/each}
|
|
69
|
+
{:else}
|
|
70
|
+
<Body size="XS" color="var(--spectrum-global-color-gray-500)">
|
|
71
|
+
No agents
|
|
72
|
+
</Body>
|
|
73
|
+
{/if}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div class="list-section">
|
|
77
|
+
<div class="list-title">Recent Chats</div>
|
|
78
|
+
{#if conversationHistory.length}
|
|
79
|
+
{#each conversationHistory as conversation}
|
|
80
|
+
{#if conversation._id}
|
|
81
|
+
<button
|
|
82
|
+
class="list-item list-item-button"
|
|
83
|
+
class:selected={selectedConversationId === conversation._id}
|
|
84
|
+
on:click={() => selectConversation(conversation._id!)}
|
|
85
|
+
>
|
|
86
|
+
{conversation.title || "Untitled Chat"}
|
|
87
|
+
</button>
|
|
88
|
+
{/if}
|
|
89
|
+
{/each}
|
|
90
|
+
{:else}
|
|
91
|
+
<Body size="XS" color="var(--spectrum-global-color-gray-500)">
|
|
92
|
+
No recent chats
|
|
93
|
+
</Body>
|
|
94
|
+
{/if}
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<style>
|
|
100
|
+
.chat-nav-shell {
|
|
101
|
+
display: flex;
|
|
102
|
+
width: 260px;
|
|
103
|
+
min-width: 260px;
|
|
104
|
+
border-right: var(--border-light);
|
|
105
|
+
background: transparent;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.chat-nav-content {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--spacing-xl);
|
|
112
|
+
width: 100%;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.list-section {
|
|
116
|
+
padding: var(--spacing-m);
|
|
117
|
+
display: flex;
|
|
118
|
+
flex-direction: column;
|
|
119
|
+
gap: var(--spacing-xxs);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.list-section + .list-section {
|
|
123
|
+
padding-top: 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.new-chat {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: var(--spacing-s);
|
|
130
|
+
background: transparent;
|
|
131
|
+
border: none;
|
|
132
|
+
padding: var(--spacing-xs) 0;
|
|
133
|
+
font: inherit;
|
|
134
|
+
color: var(--spectrum-global-color-gray-700);
|
|
135
|
+
text-align: left;
|
|
136
|
+
width: 100%;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.new-chat-icon {
|
|
141
|
+
display: inline-flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
justify-content: center;
|
|
144
|
+
width: 28px;
|
|
145
|
+
height: 28px;
|
|
146
|
+
border-radius: 50%;
|
|
147
|
+
background: var(--spectrum-semantic-cta-color-background-default);
|
|
148
|
+
color: var(--spectrum-global-color-gray-50);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.new-chat-label {
|
|
152
|
+
font-size: 14px;
|
|
153
|
+
color: var(--spectrum-global-color-gray-800);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.new-chat:hover .new-chat-icon {
|
|
157
|
+
background: var(--spectrum-semantic-cta-color-background-hover);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.list-item {
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
gap: var(--spacing-s);
|
|
164
|
+
background: transparent;
|
|
165
|
+
border: none;
|
|
166
|
+
padding: var(--spacing-xs) 0;
|
|
167
|
+
font: inherit;
|
|
168
|
+
color: var(--spectrum-global-color-gray-700);
|
|
169
|
+
text-align: left;
|
|
170
|
+
width: 100%;
|
|
171
|
+
white-space: nowrap;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
text-overflow: ellipsis;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.list-item-icon {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.list-item-button {
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.list-item-button:hover {
|
|
187
|
+
color: var(--spectrum-global-color-gray-900);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.list-item.selected {
|
|
191
|
+
color: var(--spectrum-global-color-gray-900);
|
|
192
|
+
font-weight: 600;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.list-title {
|
|
196
|
+
font-size: 14px;
|
|
197
|
+
line-height: 17px;
|
|
198
|
+
letter-spacing: 0;
|
|
199
|
+
color: var(--spectrum-global-color-gray-600);
|
|
200
|
+
font-weight: 400;
|
|
201
|
+
margin-bottom: var(--spacing-xs);
|
|
202
|
+
}
|
|
203
|
+
</style>
|
|
@@ -758,7 +758,7 @@
|
|
|
758
758
|
.message.user {
|
|
759
759
|
border-radius: 8px;
|
|
760
760
|
align-self: flex-end;
|
|
761
|
-
background-color:
|
|
761
|
+
background-color: var(--spectrum-alias-background-color-secondary);
|
|
762
762
|
font-size: 14px;
|
|
763
763
|
color: var(--spectrum-global-color-gray-800);
|
|
764
764
|
}
|
|
@@ -805,7 +805,7 @@
|
|
|
805
805
|
padding: 20px;
|
|
806
806
|
font-size: 16px;
|
|
807
807
|
background-color: var(--spectrum-global-color-gray-200);
|
|
808
|
-
color: var(--
|
|
808
|
+
color: var(--spectrum-alias-text-color);
|
|
809
809
|
border-radius: 10px;
|
|
810
810
|
border: 1px solid var(--spectrum-global-color-gray-300) !important;
|
|
811
811
|
outline: none;
|
|
@@ -813,7 +813,7 @@
|
|
|
813
813
|
}
|
|
814
814
|
|
|
815
815
|
.input:focus {
|
|
816
|
-
border: 1px solid
|
|
816
|
+
border: 1px solid var(--spectrum-alias-border-color-mouse-focus) !important;
|
|
817
817
|
}
|
|
818
818
|
|
|
819
819
|
.input::placeholder {
|
package/src/components/index.ts
CHANGED
|
@@ -11,3 +11,5 @@ 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"
|