@budibase/frontend-core 3.41.3 → 3.43.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/chatApps.ts +12 -1
- package/src/api/index.ts +2 -0
- package/src/api/restTemplates.ts +74 -0
- package/src/api/types.ts +2 -0
- package/src/components/Chatbox/index.svelte +29 -0
- package/src/components/Chatbox/promptHistory.test.ts +85 -0
- package/src/components/Chatbox/promptHistory.ts +41 -0
- package/src/components/UserAvatars.svelte +1 -1
- package/src/components/grid/cells/DateCell.svelte +1 -1
- package/src/styles.d.ts +2 -0
- package/tsconfig.json +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "v3.43.0",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"vitest": "^4.1.0"
|
|
25
25
|
},
|
|
26
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "221c199a898fffd9c8803d5327cc3089cc02b7f3"
|
|
27
27
|
}
|
package/src/api/chatApps.ts
CHANGED
|
@@ -55,6 +55,14 @@ export interface ChatAppEndpoints {
|
|
|
55
55
|
>
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const getBrowserTimezone = () => {
|
|
59
|
+
try {
|
|
60
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
61
|
+
} catch {
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
const throwOnErrorChunk = () =>
|
|
59
67
|
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
60
68
|
transform(chunk, controller) {
|
|
@@ -82,7 +90,10 @@ export const buildChatAppEndpoints = (
|
|
|
82
90
|
throw new Error("chatAppId is required to stream a chat conversation")
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
const body: ChatAgentRequest =
|
|
93
|
+
const body: ChatAgentRequest = {
|
|
94
|
+
...chat,
|
|
95
|
+
timezone: chat.timezone ?? getBrowserTimezone(),
|
|
96
|
+
}
|
|
86
97
|
const conversationId = chat._id || "new"
|
|
87
98
|
|
|
88
99
|
const response = await fetch(
|
package/src/api/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { buildFeatureFlagEndpoints } from "./features"
|
|
|
58
58
|
import { buildNavigationEndpoints } from "./navigation"
|
|
59
59
|
import { buildWorkspaceAppEndpoints } from "./workspaceApps"
|
|
60
60
|
import { buildResourceEndpoints } from "./resource"
|
|
61
|
+
import { buildRestTemplateEndpoints } from "./restTemplates"
|
|
61
62
|
import { buildDeploymentEndpoints } from "./deploy"
|
|
62
63
|
import { buildWorkspaceFavouriteEndpoints } from "./workspaceFavourites"
|
|
63
64
|
import { buildWorkspaceHomeEndpoints } from "./workspaceHome"
|
|
@@ -309,6 +310,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
|
|
|
309
310
|
...buildScreenEndpoints(API),
|
|
310
311
|
...buildTableEndpoints(API),
|
|
311
312
|
...buildTemplateEndpoints(API),
|
|
313
|
+
...buildRestTemplateEndpoints(API),
|
|
312
314
|
...buildUserEndpoints(API),
|
|
313
315
|
...buildViewEndpoints(API),
|
|
314
316
|
...buildSelfEndpoints(API),
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CustomRestTemplateId,
|
|
3
|
+
DeleteCustomRestTemplateResponse,
|
|
4
|
+
FetchCustomRestTemplatesResponse,
|
|
5
|
+
UpdateCustomRestTemplateResponse,
|
|
6
|
+
UploadCustomRestTemplateResponse,
|
|
7
|
+
} from "@budibase/types"
|
|
8
|
+
import type { BaseAPIClient } from "./types"
|
|
9
|
+
|
|
10
|
+
interface UploadCustomRestTemplateParams {
|
|
11
|
+
name: string
|
|
12
|
+
description: string
|
|
13
|
+
file: File
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface UpdateCustomRestTemplateParams {
|
|
17
|
+
restTemplateId: CustomRestTemplateId
|
|
18
|
+
name: string
|
|
19
|
+
description: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RestTemplateEndpoints {
|
|
23
|
+
getCustomRestTemplates: () => Promise<FetchCustomRestTemplatesResponse>
|
|
24
|
+
uploadCustomRestTemplate: (
|
|
25
|
+
params: UploadCustomRestTemplateParams
|
|
26
|
+
) => Promise<UploadCustomRestTemplateResponse>
|
|
27
|
+
updateCustomRestTemplate: (
|
|
28
|
+
params: UpdateCustomRestTemplateParams
|
|
29
|
+
) => Promise<UpdateCustomRestTemplateResponse>
|
|
30
|
+
deleteCustomRestTemplate: (
|
|
31
|
+
restTemplateId: CustomRestTemplateId
|
|
32
|
+
) => Promise<DeleteCustomRestTemplateResponse>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const buildRestTemplateEndpoints = (
|
|
36
|
+
API: BaseAPIClient
|
|
37
|
+
): RestTemplateEndpoints => ({
|
|
38
|
+
getCustomRestTemplates: async () => {
|
|
39
|
+
return await API.get({
|
|
40
|
+
url: "/api/rest-templates",
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
uploadCustomRestTemplate: async ({ name, description, file }) => {
|
|
45
|
+
const body = new FormData()
|
|
46
|
+
body.append("name", name)
|
|
47
|
+
body.append("description", description)
|
|
48
|
+
body.append("file", file)
|
|
49
|
+
|
|
50
|
+
return await API.post<FormData, UploadCustomRestTemplateResponse>({
|
|
51
|
+
url: "/api/rest-templates",
|
|
52
|
+
body,
|
|
53
|
+
json: false,
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
updateCustomRestTemplate: async ({ restTemplateId, name, description }) => {
|
|
58
|
+
const body = new FormData()
|
|
59
|
+
body.append("name", name)
|
|
60
|
+
body.append("description", description)
|
|
61
|
+
|
|
62
|
+
return await API.put<FormData, UpdateCustomRestTemplateResponse>({
|
|
63
|
+
url: `/api/rest-templates/${encodeURIComponent(restTemplateId)}`,
|
|
64
|
+
body,
|
|
65
|
+
json: false,
|
|
66
|
+
})
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
deleteCustomRestTemplate: async restTemplateId => {
|
|
70
|
+
return await API.delete({
|
|
71
|
+
url: `/api/rest-templates/${encodeURIComponent(restTemplateId)}`,
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
})
|
package/src/api/types.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { EscalationEndpoints } from "./escalations"
|
|
|
45
45
|
import { NavigationEndpoints } from "./navigation"
|
|
46
46
|
import { WorkspaceAppEndpoints } from "./workspaceApps"
|
|
47
47
|
import { ResourceEndpoints } from "./resource"
|
|
48
|
+
import { RestTemplateEndpoints } from "./restTemplates"
|
|
48
49
|
import { DeploymentEndpoints } from "./deploy"
|
|
49
50
|
import { WorkspaceFavouriteEndpoints } from "./workspaceFavourites"
|
|
50
51
|
import { WorkspaceHomeEndpoints } from "./workspaceHome"
|
|
@@ -159,6 +160,7 @@ export type APIClient = BaseAPIClient &
|
|
|
159
160
|
SelfEndpoints &
|
|
160
161
|
TableEndpoints &
|
|
161
162
|
TemplateEndpoints &
|
|
163
|
+
RestTemplateEndpoints &
|
|
162
164
|
UserEndpoints &
|
|
163
165
|
FeatureFlagEndpoints &
|
|
164
166
|
ViewEndpoints & {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import ReasoningStatus from "./ReasoningStatus.svelte"
|
|
24
24
|
import ContextUsage from "./ContextUsage.svelte"
|
|
25
25
|
import EscalationCard from "./EscalationCard.svelte"
|
|
26
|
+
import { navigatePromptHistory } from "./promptHistory"
|
|
26
27
|
import {
|
|
27
28
|
DefaultChatTransport,
|
|
28
29
|
isTextUIPart,
|
|
@@ -57,8 +58,11 @@
|
|
|
57
58
|
accepted: boolean
|
|
58
59
|
) => Promise<EscalationRespondResult | undefined>
|
|
59
60
|
isAgentPreviewChat?: boolean
|
|
61
|
+
previewRoleId?: string
|
|
60
62
|
readOnly?: boolean
|
|
61
63
|
readOnlyReason?: "disabled" | "deleted" | "offline"
|
|
64
|
+
promptHistory?: string[]
|
|
65
|
+
onpromptsubmitted?: (prompt: string) => void
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
let {
|
|
@@ -73,8 +77,11 @@
|
|
|
73
77
|
showInlineApproval = false,
|
|
74
78
|
onResolve,
|
|
75
79
|
isAgentPreviewChat = false,
|
|
80
|
+
previewRoleId,
|
|
76
81
|
readOnly = false,
|
|
77
82
|
readOnlyReason,
|
|
83
|
+
promptHistory = [],
|
|
84
|
+
onpromptsubmitted,
|
|
78
85
|
}: Props = $props()
|
|
79
86
|
|
|
80
87
|
// Per-escalation in-flight flag + the message relayed from resolve, so the
|
|
@@ -133,6 +140,7 @@
|
|
|
133
140
|
let expandedTools = $state<Record<string, boolean>>({})
|
|
134
141
|
let reasoningTextByMessageId = $state<Record<string, string>>({})
|
|
135
142
|
let inputValue = $state("")
|
|
143
|
+
let promptHistoryIndex = $state<number | undefined>()
|
|
136
144
|
let lastInitialPrompt = $state("")
|
|
137
145
|
let isPreparingResponse = $state(false)
|
|
138
146
|
const resetPendingResponse = () => {
|
|
@@ -325,6 +333,7 @@
|
|
|
325
333
|
agentId: chat?.agentId,
|
|
326
334
|
transient: !persistConversation,
|
|
327
335
|
isPreview: isAgentPreviewChat,
|
|
336
|
+
previewRoleId: isAgentPreviewChat ? previewRoleId : undefined,
|
|
328
337
|
sessionId: stableSessionId,
|
|
329
338
|
title: chat?.title,
|
|
330
339
|
messages,
|
|
@@ -560,6 +569,23 @@
|
|
|
560
569
|
return
|
|
561
570
|
}
|
|
562
571
|
|
|
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
|
+
|
|
563
589
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
564
590
|
event.preventDefault()
|
|
565
591
|
await sendMessage()
|
|
@@ -634,6 +660,8 @@
|
|
|
634
660
|
}
|
|
635
661
|
|
|
636
662
|
inputValue = ""
|
|
663
|
+
promptHistoryIndex = undefined
|
|
664
|
+
onpromptsubmitted?.(text)
|
|
637
665
|
chatInstance.sendMessage({ text })
|
|
638
666
|
isPreparingResponse = false
|
|
639
667
|
}
|
|
@@ -955,6 +983,7 @@
|
|
|
955
983
|
bind:this={textareaElement}
|
|
956
984
|
class="input spectrum-Textfield-input"
|
|
957
985
|
onkeydown={handleKeyDown}
|
|
986
|
+
oninput={() => (promptHistoryIndex = undefined)}
|
|
958
987
|
placeholder="Ask..."
|
|
959
988
|
disabled={isRequestPending}
|
|
960
989
|
></textarea>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import { navigatePromptHistory } from "./promptHistory"
|
|
3
|
+
|
|
4
|
+
describe("navigatePromptHistory", () => {
|
|
5
|
+
const history = ["first", "second", "third"]
|
|
6
|
+
|
|
7
|
+
it("starts at the newest prompt from an empty input", () => {
|
|
8
|
+
expect(
|
|
9
|
+
navigatePromptHistory({
|
|
10
|
+
key: "ArrowUp",
|
|
11
|
+
history,
|
|
12
|
+
inputValue: "",
|
|
13
|
+
index: undefined,
|
|
14
|
+
})
|
|
15
|
+
).toEqual({ inputValue: "third", index: 2 })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it("navigates backwards and stops at the oldest prompt", () => {
|
|
19
|
+
expect(
|
|
20
|
+
navigatePromptHistory({
|
|
21
|
+
key: "ArrowUp",
|
|
22
|
+
history,
|
|
23
|
+
inputValue: "second",
|
|
24
|
+
index: 1,
|
|
25
|
+
})
|
|
26
|
+
).toEqual({ inputValue: "first", index: 0 })
|
|
27
|
+
expect(
|
|
28
|
+
navigatePromptHistory({
|
|
29
|
+
key: "ArrowUp",
|
|
30
|
+
history,
|
|
31
|
+
inputValue: "first",
|
|
32
|
+
index: 0,
|
|
33
|
+
})
|
|
34
|
+
).toEqual({ inputValue: "first", index: 0 })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("navigates forwards and clears after the newest prompt", () => {
|
|
38
|
+
expect(
|
|
39
|
+
navigatePromptHistory({
|
|
40
|
+
key: "ArrowDown",
|
|
41
|
+
history,
|
|
42
|
+
inputValue: "first",
|
|
43
|
+
index: 0,
|
|
44
|
+
})
|
|
45
|
+
).toEqual({ inputValue: "second", index: 1 })
|
|
46
|
+
expect(
|
|
47
|
+
navigatePromptHistory({
|
|
48
|
+
key: "ArrowDown",
|
|
49
|
+
history,
|
|
50
|
+
inputValue: "third",
|
|
51
|
+
index: 2,
|
|
52
|
+
})
|
|
53
|
+
).toEqual({ inputValue: "", index: undefined })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("leaves arrow keys alone until navigation starts", () => {
|
|
57
|
+
expect(
|
|
58
|
+
navigatePromptHistory({
|
|
59
|
+
key: "ArrowUp",
|
|
60
|
+
history,
|
|
61
|
+
inputValue: "draft",
|
|
62
|
+
index: undefined,
|
|
63
|
+
})
|
|
64
|
+
).toBeUndefined()
|
|
65
|
+
expect(
|
|
66
|
+
navigatePromptHistory({
|
|
67
|
+
key: "ArrowDown",
|
|
68
|
+
history,
|
|
69
|
+
inputValue: "",
|
|
70
|
+
index: undefined,
|
|
71
|
+
})
|
|
72
|
+
).toBeUndefined()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("leaves Enter handling to the chat input", () => {
|
|
76
|
+
expect(
|
|
77
|
+
navigatePromptHistory({
|
|
78
|
+
key: "Enter",
|
|
79
|
+
history,
|
|
80
|
+
inputValue: "draft",
|
|
81
|
+
index: undefined,
|
|
82
|
+
})
|
|
83
|
+
).toBeUndefined()
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface PromptHistoryNavigationState {
|
|
2
|
+
inputValue: string
|
|
3
|
+
index: number | undefined
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface NavigatePromptHistoryOptions extends PromptHistoryNavigationState {
|
|
7
|
+
key: string
|
|
8
|
+
history: string[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const navigatePromptHistory = ({
|
|
12
|
+
key,
|
|
13
|
+
history,
|
|
14
|
+
inputValue,
|
|
15
|
+
index,
|
|
16
|
+
}: NavigatePromptHistoryOptions): PromptHistoryNavigationState | undefined => {
|
|
17
|
+
if (key !== "ArrowUp" && key !== "ArrowDown") {
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (index === undefined) {
|
|
22
|
+
if (key !== "ArrowUp" || inputValue !== "" || history.length === 0) {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const nextIndex = history.length - 1
|
|
27
|
+
return { inputValue: history[nextIndex], index: nextIndex }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (key === "ArrowUp") {
|
|
31
|
+
const nextIndex = Math.max(0, index - 1)
|
|
32
|
+
return { inputValue: history[nextIndex], index: nextIndex }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (index >= history.length - 1) {
|
|
36
|
+
return { inputValue: "", index: undefined }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const nextIndex = index + 1
|
|
40
|
+
return { inputValue: history[nextIndex], index: nextIndex }
|
|
41
|
+
}
|
package/src/styles.d.ts
ADDED
package/tsconfig.json
CHANGED
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
"module": "preserve",
|
|
7
7
|
"moduleResolution": "bundler",
|
|
8
8
|
"outDir": "./dist",
|
|
9
|
+
"rootDir": "..",
|
|
9
10
|
"skipLibCheck": true,
|
|
10
|
-
"allowJs": true
|
|
11
|
+
"allowJs": true,
|
|
12
|
+
"paths": {
|
|
13
|
+
"@budibase/*": ["../*/src"],
|
|
14
|
+
"@/*": ["../bbui/src/*"]
|
|
15
|
+
}
|
|
11
16
|
},
|
|
12
17
|
"include": ["src/**/*"],
|
|
13
18
|
"exclude": ["node_modules", "dist"]
|