@budibase/frontend-core 3.23.47 → 3.24.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 +3 -3
- package/src/api/agents.ts +10 -79
- package/src/api/chatApps.ts +190 -0
- package/src/api/index.ts +2 -0
- package/src/api/plugins.ts +24 -0
- package/src/api/types.ts +2 -0
- package/src/components/Chatbox/index.svelte +103 -11
- package/src/components/ClientAppSkeleton.svelte +8 -8
- package/src/components/grid/cells/AICell.svelte +1 -1
- package/src/components/grid/cells/AttachmentCell.svelte +8 -3
- package/src/components/grid/cells/LongFormCell.svelte +1 -1
- package/src/components/grid/cells/SignatureCell.svelte +9 -4
- package/src/components/grid/layout/ButtonColumn.svelte +7 -7
- package/src/components/grid/layout/GridBody.svelte +2 -2
- package/src/components/grid/layout/GridRow.svelte +2 -2
- package/src/components/grid/layout/NewRow.svelte +8 -5
- package/src/components/grid/overlays/MenuOverlay.svelte +1 -1
- package/src/components/grid/overlays/PopoverOverlay.svelte +1 -1
- package/src/components/grid/overlays/ReorderOverlay.svelte +1 -1
- package/src/components/grid/overlays/ResizeOverlay.svelte +1 -1
- package/src/components/grid/overlays/ScrollOverlay.svelte +2 -2
- package/src/components/grid/stores/rows.ts +16 -5
- package/src/fetch/JSONArrayFetch.ts +5 -1
- package/svelte.config.mjs +1 -1
- /package/src/components/{index.js → index.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"@budibase/bbui": "*",
|
|
13
13
|
"@budibase/shared-core": "*",
|
|
14
14
|
"@budibase/types": "*",
|
|
15
|
-
"ai": "^
|
|
15
|
+
"ai": "^6.0.3",
|
|
16
16
|
"dayjs": "^1.10.8",
|
|
17
17
|
"lodash": "4.17.21",
|
|
18
18
|
"shortid": "2.2.15",
|
|
19
19
|
"socket.io-client": "^4.7.5"
|
|
20
20
|
},
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "7cfa950d39730aa88a1c26c60f8094d6f0b0606d"
|
|
22
22
|
}
|
package/src/api/agents.ts
CHANGED
|
@@ -1,101 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AgentChat,
|
|
3
|
-
ChatAgentRequest,
|
|
4
2
|
CreateAgentRequest,
|
|
5
3
|
CreateAgentResponse,
|
|
6
|
-
FetchAgentHistoryResponse,
|
|
7
4
|
FetchAgentsResponse,
|
|
8
5
|
ToolMetadata,
|
|
9
6
|
UpdateAgentRequest,
|
|
10
7
|
UpdateAgentResponse,
|
|
11
8
|
} from "@budibase/types"
|
|
12
9
|
|
|
13
|
-
import { Header } from "@budibase/shared-core"
|
|
14
10
|
import { BaseAPIClient } from "./types"
|
|
15
|
-
import { readUIMessageStream, UIMessage, UIMessageChunk } from "ai"
|
|
16
|
-
import { createSseToJsonTransformStream } from "../utils/utils"
|
|
17
11
|
|
|
18
12
|
export interface AgentEndpoints {
|
|
19
|
-
|
|
20
|
-
chat: AgentChat,
|
|
21
|
-
workspaceId: string
|
|
22
|
-
) => Promise<AsyncIterable<UIMessage>>
|
|
23
|
-
|
|
24
|
-
removeChat: (chatId: string) => Promise<void>
|
|
25
|
-
fetchChats: (agentId: string) => Promise<FetchAgentHistoryResponse>
|
|
26
|
-
|
|
27
|
-
fetchTools: () => Promise<ToolMetadata[]>
|
|
13
|
+
fetchTools: (aiconfigId?: string) => Promise<ToolMetadata[]>
|
|
28
14
|
fetchAgents: () => Promise<FetchAgentsResponse>
|
|
29
15
|
createAgent: (agent: CreateAgentRequest) => Promise<CreateAgentResponse>
|
|
30
16
|
updateAgent: (agent: UpdateAgentRequest) => Promise<UpdateAgentResponse>
|
|
31
17
|
deleteAgent: (agentId: string) => Promise<{ deleted: true }>
|
|
32
18
|
}
|
|
33
19
|
|
|
34
|
-
const throwOnErrorChunk = () =>
|
|
35
|
-
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
36
|
-
transform(chunk, controller) {
|
|
37
|
-
if (chunk.type === "error") {
|
|
38
|
-
throw new Error(chunk.errorText || "Agent action failed")
|
|
39
|
-
}
|
|
40
|
-
controller.enqueue(chunk)
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
|
|
44
20
|
export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
|
|
45
|
-
agentChatStream: async (chat, workspaceId) => {
|
|
46
|
-
const body: ChatAgentRequest = chat
|
|
47
|
-
|
|
48
|
-
const response = await fetch("/api/agent/chat/stream", {
|
|
49
|
-
method: "POST",
|
|
50
|
-
headers: {
|
|
51
|
-
"Content-Type": "application/json",
|
|
52
|
-
Accept: "application/json",
|
|
53
|
-
[Header.APP_ID]: workspaceId,
|
|
54
|
-
},
|
|
55
|
-
body: JSON.stringify(body),
|
|
56
|
-
credentials: "same-origin",
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
if (!response.ok) {
|
|
60
|
-
const errorBody = await response.json()
|
|
61
|
-
throw new Error(
|
|
62
|
-
errorBody.message || `HTTP error! status: ${response.status}`
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!response.body) {
|
|
67
|
-
throw new Error("Failed to get response body")
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const chunkStream = response.body
|
|
71
|
-
.pipeThrough(new TextDecoderStream())
|
|
72
|
-
.pipeThrough(createSseToJsonTransformStream<UIMessageChunk>())
|
|
73
|
-
.pipeThrough(throwOnErrorChunk())
|
|
74
|
-
|
|
75
|
-
return readUIMessageStream({
|
|
76
|
-
stream: chunkStream,
|
|
77
|
-
terminateOnError: true,
|
|
78
|
-
})
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
removeChat: async (chatId: string) => {
|
|
82
|
-
return await API.delete({
|
|
83
|
-
url: `/api/agent/chats/${chatId}`,
|
|
84
|
-
})
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
fetchChats: async (agentId: string) => {
|
|
88
|
-
return await API.get({
|
|
89
|
-
url: `/api/agent/${agentId}/chats`,
|
|
90
|
-
})
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
fetchTools: async () => {
|
|
94
|
-
return await API.get({
|
|
95
|
-
url: `/api/agent/tools`,
|
|
96
|
-
})
|
|
97
|
-
},
|
|
98
|
-
|
|
99
21
|
fetchAgents: async () => {
|
|
100
22
|
return await API.get({
|
|
101
23
|
url: "/api/agent",
|
|
@@ -121,4 +43,13 @@ export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
|
|
|
121
43
|
url: `/api/agent/${agentId}`,
|
|
122
44
|
})
|
|
123
45
|
},
|
|
46
|
+
|
|
47
|
+
fetchTools: async (aiconfigId?: string) => {
|
|
48
|
+
const query = aiconfigId
|
|
49
|
+
? `?aiconfigId=${encodeURIComponent(aiconfigId)}`
|
|
50
|
+
: ""
|
|
51
|
+
return await API.get({
|
|
52
|
+
url: `/api/agent/tools${query}`,
|
|
53
|
+
})
|
|
54
|
+
},
|
|
124
55
|
})
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatAgentRequest,
|
|
3
|
+
ChatConversation,
|
|
4
|
+
ChatConversationRequest,
|
|
5
|
+
CreateChatConversationRequest,
|
|
6
|
+
ChatApp,
|
|
7
|
+
FetchAgentHistoryResponse,
|
|
8
|
+
UpdateChatAppRequest,
|
|
9
|
+
} from "@budibase/types"
|
|
10
|
+
import { Header } from "@budibase/shared-core"
|
|
11
|
+
import { BaseAPIClient } from "./types"
|
|
12
|
+
import { readUIMessageStream, UIMessage, UIMessageChunk } from "ai"
|
|
13
|
+
import { createSseToJsonTransformStream } from "../utils/utils"
|
|
14
|
+
|
|
15
|
+
export interface ChatAppEndpoints {
|
|
16
|
+
streamChatConversation: (
|
|
17
|
+
chat: ChatConversationRequest,
|
|
18
|
+
workspaceId: string
|
|
19
|
+
) => Promise<AsyncIterable<UIMessage>>
|
|
20
|
+
deleteChatConversation: (
|
|
21
|
+
chatConversationId: string,
|
|
22
|
+
chatAppId: string
|
|
23
|
+
) => Promise<void>
|
|
24
|
+
fetchChatConversation: (
|
|
25
|
+
chatAppId: string,
|
|
26
|
+
chatConversationId: string
|
|
27
|
+
) => Promise<ChatConversation>
|
|
28
|
+
fetchChatHistory: (chatAppId: string) => Promise<FetchAgentHistoryResponse>
|
|
29
|
+
fetchChatApp: (workspaceId?: string) => Promise<ChatApp | null>
|
|
30
|
+
setChatAppAgent: (chatAppId: string, agentId: string) => Promise<ChatApp>
|
|
31
|
+
createChatConversation: (
|
|
32
|
+
chat: CreateChatConversationRequest,
|
|
33
|
+
workspaceId?: string
|
|
34
|
+
) => Promise<ChatConversation>
|
|
35
|
+
updateChatApp: (chatApp: UpdateChatAppRequest) => Promise<ChatApp>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const throwOnErrorChunk = () =>
|
|
39
|
+
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
40
|
+
transform(chunk, controller) {
|
|
41
|
+
if (chunk.type === "error") {
|
|
42
|
+
throw new Error(chunk.errorText || "Agent action failed")
|
|
43
|
+
}
|
|
44
|
+
controller.enqueue(chunk)
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const buildChatAppEndpoints = (
|
|
49
|
+
API: BaseAPIClient
|
|
50
|
+
): ChatAppEndpoints => ({
|
|
51
|
+
streamChatConversation: async (chat, workspaceId) => {
|
|
52
|
+
if (!chat.chatAppId) {
|
|
53
|
+
throw new Error("chatAppId is required to stream a chat conversation")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const body: ChatAgentRequest = chat
|
|
57
|
+
const conversationId = chat._id || "new"
|
|
58
|
+
|
|
59
|
+
const response = await fetch(
|
|
60
|
+
`/api/chatapps/${chat.chatAppId}/conversations/${conversationId}/stream`,
|
|
61
|
+
{
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
Accept: "application/json",
|
|
66
|
+
[Header.APP_ID]: workspaceId,
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
credentials: "same-origin",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const errorBody = await response.json()
|
|
75
|
+
throw new Error(
|
|
76
|
+
errorBody.message || `HTTP error! status: ${response.status}`
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!response.body) {
|
|
81
|
+
throw new Error("Failed to get response body")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const chunkStream = response.body
|
|
85
|
+
.pipeThrough(new TextDecoderStream())
|
|
86
|
+
.pipeThrough(createSseToJsonTransformStream<UIMessageChunk>())
|
|
87
|
+
.pipeThrough(throwOnErrorChunk())
|
|
88
|
+
|
|
89
|
+
return readUIMessageStream({
|
|
90
|
+
stream: chunkStream,
|
|
91
|
+
terminateOnError: true,
|
|
92
|
+
})
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
deleteChatConversation: async (
|
|
96
|
+
chatConversationId: string,
|
|
97
|
+
chatAppId: string
|
|
98
|
+
) => {
|
|
99
|
+
return await API.delete({
|
|
100
|
+
url: `/api/chatapps/${chatAppId}/conversations/${chatConversationId}`,
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
fetchChatConversation: async (
|
|
105
|
+
chatAppId: string,
|
|
106
|
+
chatConversationId: string
|
|
107
|
+
) => {
|
|
108
|
+
return await API.get({
|
|
109
|
+
url: `/api/chatapps/${chatAppId}/conversations/${chatConversationId}`,
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
fetchChatHistory: async (chatAppId: string) => {
|
|
114
|
+
return await API.get({
|
|
115
|
+
url: `/api/chatapps/${chatAppId}/conversations`,
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
fetchChatApp: async (workspaceId?: string) => {
|
|
120
|
+
const url = "/api/chatapps"
|
|
121
|
+
const headers = workspaceId
|
|
122
|
+
? {
|
|
123
|
+
[Header.APP_ID]: workspaceId,
|
|
124
|
+
}
|
|
125
|
+
: undefined
|
|
126
|
+
return await API.get({
|
|
127
|
+
url,
|
|
128
|
+
...(headers && { headers }),
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
setChatAppAgent: async (chatAppId: string, agentId: string) => {
|
|
133
|
+
if (!chatAppId) {
|
|
134
|
+
throw new Error("chatAppId is required to set chat app agent")
|
|
135
|
+
}
|
|
136
|
+
if (!agentId) {
|
|
137
|
+
throw new Error("agentId is required to set chat app agent")
|
|
138
|
+
}
|
|
139
|
+
return await API.post({
|
|
140
|
+
url: `/api/chatapps/${chatAppId}/agent`,
|
|
141
|
+
body: { agentId } as any,
|
|
142
|
+
})
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
createChatConversation: async (
|
|
146
|
+
chat: CreateChatConversationRequest,
|
|
147
|
+
workspaceId?: string
|
|
148
|
+
) => {
|
|
149
|
+
const resolvedWorkspaceId = workspaceId || API.getAppID()
|
|
150
|
+
const { chatAppId } = chat
|
|
151
|
+
if (!chatAppId) {
|
|
152
|
+
throw new Error("chatAppId is required to create a chat conversation")
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const headers: Record<string, string> = {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
Accept: "application/json",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (resolvedWorkspaceId) {
|
|
161
|
+
headers[Header.APP_ID] = resolvedWorkspaceId
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const response = await fetch(`/api/chatapps/${chatAppId}/conversations`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers,
|
|
167
|
+
credentials: "same-origin",
|
|
168
|
+
body: JSON.stringify(chat),
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const errorBody = await response.json().catch(() => null)
|
|
173
|
+
throw new Error(
|
|
174
|
+
errorBody?.message || `HTTP error! status: ${response.status}`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return (await response.json()) as ChatConversation
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
updateChatApp: async (chatApp: UpdateChatAppRequest) => {
|
|
182
|
+
if (!chatApp._id) {
|
|
183
|
+
throw new Error("chatAppId is required to update a chat app")
|
|
184
|
+
}
|
|
185
|
+
return await API.put({
|
|
186
|
+
url: `/api/chatapps/${chatApp._id}`,
|
|
187
|
+
body: chatApp as any,
|
|
188
|
+
})
|
|
189
|
+
},
|
|
190
|
+
})
|
package/src/api/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { buildMigrationEndpoints } from "./migrations"
|
|
|
47
47
|
import { buildRowActionEndpoints } from "./rowActions"
|
|
48
48
|
import { buildOAuth2Endpoints } from "./oauth2"
|
|
49
49
|
import { buildAgentEndpoints } from "./agents"
|
|
50
|
+
import { buildChatAppEndpoints } from "./chatApps"
|
|
50
51
|
import { buildFeatureFlagEndpoints } from "./features"
|
|
51
52
|
import { buildNavigationEndpoints } from "./navigation"
|
|
52
53
|
import { buildWorkspaceAppEndpoints } from "./workspaceApps"
|
|
@@ -313,6 +314,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
|
|
|
313
314
|
...buildLogsEndpoints(API),
|
|
314
315
|
...buildMigrationEndpoints(API),
|
|
315
316
|
...buildAgentEndpoints(API),
|
|
317
|
+
...buildChatAppEndpoints(API),
|
|
316
318
|
...buildFeatureFlagEndpoints(API),
|
|
317
319
|
deployment: buildDeploymentEndpoints(API),
|
|
318
320
|
viewV2: buildViewV2Endpoints(API),
|
package/src/api/plugins.ts
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
FetchPluginResponse,
|
|
6
6
|
UploadPluginRequest,
|
|
7
7
|
UploadPluginResponse,
|
|
8
|
+
PluginUpdateCheckResponse,
|
|
9
|
+
PluginUpdateApplyRequest,
|
|
10
|
+
PluginUpdateApplyResponse,
|
|
8
11
|
} from "@budibase/types"
|
|
9
12
|
import { BaseAPIClient } from "./types"
|
|
10
13
|
|
|
@@ -13,6 +16,11 @@ export interface PluginEndpoins {
|
|
|
13
16
|
createPlugin: (data: CreatePluginRequest) => Promise<CreatePluginResponse>
|
|
14
17
|
getPlugins: () => Promise<FetchPluginResponse>
|
|
15
18
|
deletePlugin: (pluginId: string) => Promise<DeletePluginResponse>
|
|
19
|
+
checkPluginUpdates: (token?: string) => Promise<PluginUpdateCheckResponse>
|
|
20
|
+
applyPluginUpdates: (
|
|
21
|
+
data: PluginUpdateApplyRequest,
|
|
22
|
+
token?: string
|
|
23
|
+
) => Promise<PluginUpdateApplyResponse>
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
export const buildPluginEndpoints = (API: BaseAPIClient): PluginEndpoins => ({
|
|
@@ -58,4 +66,20 @@ export const buildPluginEndpoints = (API: BaseAPIClient): PluginEndpoins => ({
|
|
|
58
66
|
url: `/api/plugin/${encodeURIComponent(pluginId)}`,
|
|
59
67
|
})
|
|
60
68
|
},
|
|
69
|
+
|
|
70
|
+
checkPluginUpdates: async token => {
|
|
71
|
+
const url = token
|
|
72
|
+
? `/api/plugin/updates?token=${encodeURIComponent(token)}`
|
|
73
|
+
: "/api/plugin/updates"
|
|
74
|
+
return await API.get({
|
|
75
|
+
url,
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
applyPluginUpdates: async (data, token) => {
|
|
80
|
+
return await API.post({
|
|
81
|
+
url: "/api/plugin/updates",
|
|
82
|
+
body: { ...data, token },
|
|
83
|
+
})
|
|
84
|
+
},
|
|
61
85
|
})
|
package/src/api/types.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { UserEndpoints } from "./user"
|
|
|
35
35
|
import { ViewEndpoints } from "./views"
|
|
36
36
|
import { ViewV2Endpoints } from "./viewsV2"
|
|
37
37
|
import { AgentEndpoints } from "./agents"
|
|
38
|
+
import { ChatAppEndpoints } from "./chatApps"
|
|
38
39
|
import { NavigationEndpoints } from "./navigation"
|
|
39
40
|
import { WorkspaceAppEndpoints } from "./workspaceApps"
|
|
40
41
|
import { ResourceEndpoints } from "./resource"
|
|
@@ -113,6 +114,7 @@ export type APIError = {
|
|
|
113
114
|
export type APIClient = BaseAPIClient &
|
|
114
115
|
AIEndpoints &
|
|
115
116
|
AgentEndpoints &
|
|
117
|
+
ChatAppEndpoints &
|
|
116
118
|
AnalyticsEndpoints &
|
|
117
119
|
AppEndpoints &
|
|
118
120
|
AttachmentEndpoints &
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { MarkdownViewer, notifications } from "@budibase/bbui"
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ChatConversation,
|
|
5
|
+
ChatConversationRequest,
|
|
6
|
+
} from "@budibase/types"
|
|
7
|
+
import { Header } from "@budibase/shared-core"
|
|
4
8
|
import BBAI from "../../icons/BBAI.svelte"
|
|
5
9
|
import { tick } from "svelte"
|
|
6
10
|
import { onDestroy } from "svelte"
|
|
@@ -10,14 +14,21 @@
|
|
|
10
14
|
import type { UIMessage } from "ai"
|
|
11
15
|
import { v4 as uuidv4 } from "uuid"
|
|
12
16
|
|
|
13
|
-
export let API = createAPIClient()
|
|
14
|
-
|
|
15
17
|
export let workspaceId: string
|
|
16
|
-
export let
|
|
18
|
+
export let API = createAPIClient({
|
|
19
|
+
attachHeaders: headers => {
|
|
20
|
+
if (workspaceId) {
|
|
21
|
+
headers[Header.APP_ID] = workspaceId
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
type ChatConversationLike = ChatConversation | ChatConversationRequest
|
|
26
|
+
|
|
27
|
+
export let chat: ChatConversationLike
|
|
17
28
|
export let loading: boolean = false
|
|
18
29
|
|
|
19
30
|
const dispatch = createEventDispatcher<{
|
|
20
|
-
chatSaved: { chatId?: string; chat:
|
|
31
|
+
chatSaved: { chatId?: string; chat: ChatConversationLike }
|
|
21
32
|
}>()
|
|
22
33
|
|
|
23
34
|
let inputValue = ""
|
|
@@ -25,12 +36,32 @@
|
|
|
25
36
|
let observer: MutationObserver
|
|
26
37
|
let textareaElement: HTMLTextAreaElement
|
|
27
38
|
let lastFocusedChatId: string | undefined
|
|
28
|
-
let lastFocusedNewChat:
|
|
39
|
+
let lastFocusedNewChat: ChatConversationLike | undefined
|
|
29
40
|
|
|
30
41
|
$: if (chat?.messages?.length) {
|
|
31
42
|
scrollToBottom()
|
|
32
43
|
}
|
|
33
44
|
|
|
45
|
+
const ensureChatApp = async (): Promise<string | undefined> => {
|
|
46
|
+
if (chat?.chatAppId) {
|
|
47
|
+
return chat.chatAppId
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const chatApp = await API.fetchChatApp(workspaceId)
|
|
51
|
+
if (chatApp?._id) {
|
|
52
|
+
const baseChat = chat || { title: "", messages: [], chatAppId: "" }
|
|
53
|
+
chat = {
|
|
54
|
+
...baseChat,
|
|
55
|
+
chatAppId: chatApp._id,
|
|
56
|
+
}
|
|
57
|
+
return chatApp._id
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(err)
|
|
61
|
+
}
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
|
|
34
65
|
async function scrollToBottom() {
|
|
35
66
|
await tick()
|
|
36
67
|
if (chatAreaElement) {
|
|
@@ -46,8 +77,41 @@
|
|
|
46
77
|
}
|
|
47
78
|
|
|
48
79
|
async function prompt() {
|
|
80
|
+
const resolvedChatAppId = await ensureChatApp()
|
|
81
|
+
|
|
49
82
|
if (!chat) {
|
|
50
|
-
chat = { title: "", messages: [] }
|
|
83
|
+
chat = { title: "", messages: [], chatAppId: "" }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const chatAppId = chat.chatAppId || resolvedChatAppId
|
|
87
|
+
|
|
88
|
+
if (!chatAppId) {
|
|
89
|
+
notifications.error("Chat app could not be created")
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!chat._id && (!chat.messages || chat.messages.length === 0)) {
|
|
94
|
+
try {
|
|
95
|
+
const newChat = await API.createChatConversation(
|
|
96
|
+
{
|
|
97
|
+
chatAppId,
|
|
98
|
+
title: chat.title,
|
|
99
|
+
},
|
|
100
|
+
workspaceId
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
chat = {
|
|
104
|
+
...chat,
|
|
105
|
+
...newChat,
|
|
106
|
+
chatAppId,
|
|
107
|
+
}
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
console.error(err)
|
|
110
|
+
notifications.error(
|
|
111
|
+
err?.message || "Could not start a new chat conversation"
|
|
112
|
+
)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
51
115
|
}
|
|
52
116
|
|
|
53
117
|
const userMessage: UIMessage = {
|
|
@@ -56,8 +120,9 @@
|
|
|
56
120
|
parts: [{ type: "text", text: inputValue }],
|
|
57
121
|
}
|
|
58
122
|
|
|
59
|
-
const updatedChat:
|
|
123
|
+
const updatedChat: ChatConversationLike = {
|
|
60
124
|
...chat,
|
|
125
|
+
chatAppId: chat.chatAppId,
|
|
61
126
|
messages: [...chat.messages, userMessage],
|
|
62
127
|
}
|
|
63
128
|
|
|
@@ -68,16 +133,41 @@
|
|
|
68
133
|
loading = true
|
|
69
134
|
|
|
70
135
|
try {
|
|
71
|
-
const messageStream = await API.
|
|
136
|
+
const messageStream = await API.streamChatConversation(
|
|
137
|
+
updatedChat,
|
|
138
|
+
workspaceId
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
let streamedMessages: UIMessage[] = [...updatedChat.messages]
|
|
72
142
|
|
|
73
143
|
for await (const message of messageStream) {
|
|
144
|
+
streamedMessages = [...streamedMessages, message]
|
|
74
145
|
chat = {
|
|
75
146
|
...updatedChat,
|
|
76
|
-
messages:
|
|
147
|
+
messages: streamedMessages,
|
|
77
148
|
}
|
|
78
149
|
scrollToBottom()
|
|
79
150
|
}
|
|
80
151
|
|
|
152
|
+
// When a chat is created for the first time the server generates the ID.
|
|
153
|
+
// If we don't have it locally yet, retrieve the saved conversation so
|
|
154
|
+
// subsequent prompts append to the same document instead of creating a new one.
|
|
155
|
+
if (!chat._id && chat.chatAppId) {
|
|
156
|
+
try {
|
|
157
|
+
const history = await API.fetchChatHistory(chat.chatAppId)
|
|
158
|
+
const lastMessageId = chat.messages[chat.messages.length - 1]?.id
|
|
159
|
+
const savedConversation =
|
|
160
|
+
history?.find(convo =>
|
|
161
|
+
convo.messages.some(message => message.id === lastMessageId)
|
|
162
|
+
) || history?.[0]
|
|
163
|
+
if (savedConversation) {
|
|
164
|
+
chat = savedConversation
|
|
165
|
+
}
|
|
166
|
+
} catch (historyError) {
|
|
167
|
+
console.error(historyError)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
81
171
|
loading = false
|
|
82
172
|
dispatch("chatSaved", { chatId: chat._id, chat })
|
|
83
173
|
} catch (err: any) {
|
|
@@ -93,6 +183,8 @@
|
|
|
93
183
|
}
|
|
94
184
|
|
|
95
185
|
onMount(async () => {
|
|
186
|
+
await ensureChatApp()
|
|
187
|
+
|
|
96
188
|
// Ensure we always autoscroll to reveal new messages
|
|
97
189
|
observer = new MutationObserver(async () => {
|
|
98
190
|
await tick()
|
|
@@ -211,7 +303,7 @@
|
|
|
211
303
|
on:keydown={handleKeyDown}
|
|
212
304
|
placeholder="Ask anything"
|
|
213
305
|
disabled={loading}
|
|
214
|
-
|
|
306
|
+
></textarea>
|
|
215
307
|
</div>
|
|
216
308
|
</div>
|
|
217
309
|
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<div class:sideNav id="clientAppSkeletonLoader" class="skeleton">
|
|
9
|
-
<div class="animation" class:noAnimation
|
|
9
|
+
<div class="animation" class:noAnimation></div>
|
|
10
10
|
|
|
11
11
|
{#if !hideDevTools}
|
|
12
|
-
<div class="devTools"
|
|
12
|
+
<div class="devTools"></div>
|
|
13
13
|
{/if}
|
|
14
14
|
<div class="main">
|
|
15
|
-
<div class="nav"
|
|
15
|
+
<div class="nav"></div>
|
|
16
16
|
<div class="body">
|
|
17
|
-
<div class="bodyVerticalPadding"
|
|
17
|
+
<div class="bodyVerticalPadding"></div>
|
|
18
18
|
<div class="bodyHorizontal">
|
|
19
|
-
<div class="bodyHorizontalPadding"
|
|
19
|
+
<div class="bodyHorizontalPadding"></div>
|
|
20
20
|
<svg
|
|
21
21
|
class="svg"
|
|
22
22
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
mask="url(#mask)"
|
|
43
43
|
/>
|
|
44
44
|
</svg>
|
|
45
|
-
<div class="bodyHorizontalPadding"
|
|
45
|
+
<div class="bodyHorizontalPadding"></div>
|
|
46
46
|
</div>
|
|
47
|
-
<div class="bodyVerticalPadding"
|
|
47
|
+
<div class="bodyVerticalPadding"></div>
|
|
48
48
|
</div>
|
|
49
49
|
</div>
|
|
50
50
|
{#if !hideFooter}
|
|
51
|
-
<div class="footer"
|
|
51
|
+
<div class="footer"></div>
|
|
52
52
|
{/if}
|
|
53
53
|
</div>
|
|
54
54
|
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
export let schema
|
|
13
13
|
export let maximum
|
|
14
14
|
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
API,
|
|
17
|
+
notifications,
|
|
18
|
+
props: gridProps,
|
|
19
|
+
datasource,
|
|
20
|
+
} = getContext("grid")
|
|
16
21
|
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
|
17
22
|
|
|
18
23
|
let isOpen = false
|
|
@@ -89,7 +94,7 @@
|
|
|
89
94
|
{#each value || [] as attachment}
|
|
90
95
|
{#if isImage(attachment.extension)}
|
|
91
96
|
<img
|
|
92
|
-
class:light={!$
|
|
97
|
+
class:light={!$gridProps?.darkMode &&
|
|
93
98
|
schema.type === FieldType.SIGNATURE_SINGLE}
|
|
94
99
|
src={attachment.url}
|
|
95
100
|
alt={attachment.extension}
|
|
@@ -111,7 +116,7 @@
|
|
|
111
116
|
on:change={e => onChange(e.detail)}
|
|
112
117
|
maximum={maximum || schema.constraints?.length?.maximum}
|
|
113
118
|
{processFiles}
|
|
114
|
-
handleFileTooLarge={$
|
|
119
|
+
handleFileTooLarge={$gridProps.isCloud ? handleFileTooLarge : null}
|
|
115
120
|
/>
|
|
116
121
|
</div>
|
|
117
122
|
</GridPopover>
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
export let readonly = false
|
|
12
12
|
export let api
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
API,
|
|
16
|
+
notifications,
|
|
17
|
+
props: gridProps,
|
|
18
|
+
datasource,
|
|
19
|
+
} = getContext("grid")
|
|
15
20
|
|
|
16
21
|
let isOpen = false
|
|
17
22
|
let modal
|
|
@@ -73,7 +78,7 @@
|
|
|
73
78
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
74
79
|
<div
|
|
75
80
|
class="signature-cell"
|
|
76
|
-
class:light={!$
|
|
81
|
+
class:light={!$gridProps?.darkMode}
|
|
77
82
|
class:editable
|
|
78
83
|
bind:this={anchor}
|
|
79
84
|
on:click={editable ? open : null}
|
|
@@ -88,7 +93,7 @@
|
|
|
88
93
|
onConfirm={saveSignature}
|
|
89
94
|
title={schema?.name}
|
|
90
95
|
{value}
|
|
91
|
-
darkMode={$
|
|
96
|
+
darkMode={$gridProps.darkMode}
|
|
92
97
|
bind:this={modal}
|
|
93
98
|
/>
|
|
94
99
|
|
|
@@ -98,7 +103,7 @@
|
|
|
98
103
|
{#if value?.key}
|
|
99
104
|
<div class="signature-wrap">
|
|
100
105
|
<CoreSignature
|
|
101
|
-
darkMode={$
|
|
106
|
+
darkMode={$gridProps.darkMode}
|
|
102
107
|
editable={false}
|
|
103
108
|
{value}
|
|
104
109
|
on:change={saveSignature}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const {
|
|
9
9
|
renderedRows,
|
|
10
10
|
hoveredRowId,
|
|
11
|
-
props,
|
|
11
|
+
props: gridProps,
|
|
12
12
|
width,
|
|
13
13
|
rows,
|
|
14
14
|
focusedRow,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
let container
|
|
28
28
|
|
|
29
|
-
$: buttons = getButtons($
|
|
29
|
+
$: buttons = getButtons($gridProps)
|
|
30
30
|
$: columnsWidth = $scrollableColumns.reduce(
|
|
31
31
|
(total, col) => (total += col.width),
|
|
32
32
|
0
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
class="buttons"
|
|
117
117
|
class:offset={$showVScrollbar && $showHScrollbar}
|
|
118
118
|
>
|
|
119
|
-
{#if $
|
|
119
|
+
{#if $gridProps.buttonsCollapsed}
|
|
120
120
|
{#if rowButtons.length > 0}
|
|
121
121
|
<CollapsedButtonGroup
|
|
122
122
|
buttons={makeCollapsedButtons(rowButtons, row)}
|
|
123
|
-
text={$
|
|
123
|
+
text={$gridProps.buttonsCollapsedText || "Action"}
|
|
124
124
|
align="right"
|
|
125
125
|
offset={5}
|
|
126
126
|
size="S"
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
on:mouseenter={() => ($hoveredRowId = row._id)}
|
|
129
129
|
/>
|
|
130
130
|
{:else}
|
|
131
|
-
<div class="button-placeholder-collapsed"
|
|
131
|
+
<div class="button-placeholder-collapsed"></div>
|
|
132
132
|
{/if}
|
|
133
133
|
{:else}
|
|
134
134
|
{#each rowButtons as button}
|
|
@@ -143,13 +143,13 @@
|
|
|
143
143
|
on:click={() => handleClick(button, row)}
|
|
144
144
|
>
|
|
145
145
|
{#if button.icon}
|
|
146
|
-
<i class="{button.icon} S"
|
|
146
|
+
<i class="{button.icon} S"></i>
|
|
147
147
|
{/if}
|
|
148
148
|
{button.text || "Button"}
|
|
149
149
|
</Button>
|
|
150
150
|
{/each}
|
|
151
151
|
{#if rowButtons.length === 0}
|
|
152
|
-
<div class="button-placeholder"
|
|
152
|
+
<div class="button-placeholder"></div>
|
|
153
153
|
{/if}
|
|
154
154
|
{/if}
|
|
155
155
|
</div>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
dispatch,
|
|
15
15
|
isDragging,
|
|
16
16
|
config,
|
|
17
|
-
props,
|
|
17
|
+
props: gridProps,
|
|
18
18
|
} = getContext("grid")
|
|
19
19
|
|
|
20
20
|
let body
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
</div>
|
|
67
67
|
{/if}
|
|
68
68
|
</GridScrollWrapper>
|
|
69
|
-
{#if $
|
|
69
|
+
{#if $gridProps.buttons?.length}
|
|
70
70
|
<ButtonColumn />
|
|
71
71
|
{/if}
|
|
72
72
|
</div>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
isSelectingCells,
|
|
24
24
|
selectedCellMap,
|
|
25
25
|
selectedCellCount,
|
|
26
|
-
props,
|
|
26
|
+
props: gridProps,
|
|
27
27
|
buttonColumnWidth,
|
|
28
28
|
} = getContext("grid")
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
$hoveredRowId === row._id && (!$selectedCellCount || !$isSelectingCells)
|
|
33
33
|
$: rowFocused = $focusedRow?._id === row._id
|
|
34
34
|
$: reorderSource = $reorder.sourceColumn
|
|
35
|
-
$: hasButtons = $
|
|
35
|
+
$: hasButtons = $gridProps?.buttons?.length > 0
|
|
36
36
|
$: needsButtonSpacer = hasButtons && $buttonColumnWidth > 0
|
|
37
37
|
</script>
|
|
38
38
|
|
|
@@ -188,14 +188,17 @@
|
|
|
188
188
|
class:floating={offset > 0}
|
|
189
189
|
style="--offset:{offset}px; --sticky-width:{width}px;"
|
|
190
190
|
>
|
|
191
|
-
<div
|
|
192
|
-
|
|
191
|
+
<div
|
|
192
|
+
class="underlay sticky"
|
|
193
|
+
transition:fade|local={{ duration: 130 }}
|
|
194
|
+
></div>
|
|
195
|
+
<div class="underlay" transition:fade|local={{ duration: 130 }}></div>
|
|
193
196
|
<div class="sticky-column" transition:fade|local={{ duration: 130 }}>
|
|
194
197
|
<div class="row">
|
|
195
198
|
<GutterCell expandable on:expand={addViaModal} rowHovered>
|
|
196
199
|
<Icon name="plus" color="var(--spectrum-global-color-gray-500)" />
|
|
197
200
|
{#if isAdding}
|
|
198
|
-
<div in:fade={{ duration: 130 }} class="loading-overlay"
|
|
201
|
+
<div in:fade={{ duration: 130 }} class="loading-overlay"></div>
|
|
199
202
|
{/if}
|
|
200
203
|
</GutterCell>
|
|
201
204
|
{#if $displayColumn}
|
|
@@ -214,7 +217,7 @@
|
|
|
214
217
|
<div class="readonly-overlay">Can't edit auto column</div>
|
|
215
218
|
{/if}
|
|
216
219
|
{#if isAdding}
|
|
217
|
-
<div in:fade={{ duration: 130 }} class="loading-overlay"
|
|
220
|
+
<div in:fade={{ duration: 130 }} class="loading-overlay"></div>
|
|
218
221
|
{/if}
|
|
219
222
|
</DataCell>
|
|
220
223
|
{/if}
|
|
@@ -240,7 +243,7 @@
|
|
|
240
243
|
<div class="readonly-overlay">Can't edit auto column</div>
|
|
241
244
|
{/if}
|
|
242
245
|
{#if isAdding}
|
|
243
|
-
<div in:fade={{ duration: 130 }} class="loading-overlay"
|
|
246
|
+
<div in:fade={{ duration: 130 }} class="loading-overlay"></div>
|
|
244
247
|
{/if}
|
|
245
248
|
</DataCell>
|
|
246
249
|
{/each}
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
on:mousedown={startVDragging}
|
|
124
124
|
on:touchstart={startVDragging}
|
|
125
125
|
class:dragging={isDraggingV}
|
|
126
|
-
|
|
126
|
+
></div>
|
|
127
127
|
{/if}
|
|
128
128
|
{#if $showHScrollbar}
|
|
129
129
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
on:mousedown={startHDragging}
|
|
134
134
|
on:touchstart={startHDragging}
|
|
135
135
|
class:dragging={isDraggingH}
|
|
136
|
-
|
|
136
|
+
></div>
|
|
137
137
|
{/if}
|
|
138
138
|
|
|
139
139
|
<style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writable, derived, get, Writable, Readable } from "svelte/store"
|
|
2
|
-
import { DataFetch, fetchData } from "../../../fetch"
|
|
2
|
+
import { DataFetch, DataFetchDefinition, fetchData } from "../../../fetch"
|
|
3
3
|
import { NewRowID, RowPageSize } from "../lib/constants"
|
|
4
4
|
import {
|
|
5
5
|
generateRowID,
|
|
@@ -252,7 +252,18 @@ export const createActions = (context: StoreContext): RowActionStore => {
|
|
|
252
252
|
})
|
|
253
253
|
|
|
254
254
|
// Subscribe to changes of this fetch model
|
|
255
|
-
|
|
255
|
+
type GridFetchSnapshot = {
|
|
256
|
+
rows: Row[]
|
|
257
|
+
loading: boolean
|
|
258
|
+
loaded: boolean
|
|
259
|
+
resetKey: string
|
|
260
|
+
hasNextPage: boolean
|
|
261
|
+
error: { message: string; status: number; url: string } | null
|
|
262
|
+
definition?: DataFetchDefinition | null
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const fetchStore = newFetch as unknown as Readable<GridFetchSnapshot>
|
|
266
|
+
unsubscribe = fetchStore.subscribe(async $fetch => {
|
|
256
267
|
if ($fetch.error) {
|
|
257
268
|
// Present a helpful error to the user
|
|
258
269
|
let message = "An unknown error occurred"
|
|
@@ -481,7 +492,7 @@ export const createActions = (context: StoreContext): RowActionStore => {
|
|
|
481
492
|
})
|
|
482
493
|
|
|
483
494
|
// Create rows
|
|
484
|
-
let saved = []
|
|
495
|
+
let saved: UIRow[] = []
|
|
485
496
|
let failed = 0
|
|
486
497
|
for (let i = 0; i < count; i++) {
|
|
487
498
|
try {
|
|
@@ -700,7 +711,7 @@ export const createActions = (context: StoreContext): RowActionStore => {
|
|
|
700
711
|
|
|
701
712
|
// Update rows
|
|
702
713
|
const $columnLookupMap = get(columnLookupMap)
|
|
703
|
-
let updated = []
|
|
714
|
+
let updated: UIRow[] = []
|
|
704
715
|
let failed = 0
|
|
705
716
|
for (let i = 0; i < count; i++) {
|
|
706
717
|
const rowId = rowIds[i]
|
|
@@ -779,7 +790,7 @@ export const createActions = (context: StoreContext): RowActionStore => {
|
|
|
779
790
|
if (resetRows) {
|
|
780
791
|
rowCacheMap = {}
|
|
781
792
|
}
|
|
782
|
-
let rowsToAppend = []
|
|
793
|
+
let rowsToAppend: Row[] = []
|
|
783
794
|
let newRow
|
|
784
795
|
const $hasBudibaseIdentifiers = get(hasBudibaseIdentifiers)
|
|
785
796
|
for (let i = 0; i < newRows.length; i++) {
|
|
@@ -11,9 +11,13 @@ export default class JSONArrayFetch extends FieldFetch<JSONArrayFieldDatasource>
|
|
|
11
11
|
try {
|
|
12
12
|
const { fieldName } = datasource
|
|
13
13
|
const table = await this.API.fetchTableDefinition(datasource.tableId)
|
|
14
|
+
|
|
15
|
+
// For Postgres JSON columns, the schema may be directly available on the field
|
|
16
|
+
// Otherwise, fall back to fetching the schema
|
|
14
17
|
const schema =
|
|
15
|
-
table.schema[fieldName]
|
|
18
|
+
table.schema?.[fieldName]?.schema ??
|
|
16
19
|
getJSONArrayDatasourceSchema(table.schema, datasource)
|
|
20
|
+
|
|
17
21
|
return { schema }
|
|
18
22
|
} catch (error) {
|
|
19
23
|
return null
|
package/svelte.config.mjs
CHANGED
|
File without changes
|