@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "3.41.3",
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": "dd1b8d888c49ad09b82b00fc5b93ceff707bbd6a"
26
+ "gitHead": "221c199a898fffd9c8803d5327cc3089cc02b7f3"
27
27
  }
@@ -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 = chat
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
+ }
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- import { UserAvatar } from "@budibase/frontend-core"
3
2
  import { TooltipPosition, Avatar } from "@budibase/bbui"
4
3
  import type { UIUser } from "@budibase/types"
4
+ import UserAvatar from "./UserAvatar.svelte"
5
5
 
6
6
  type OrderType = "ltr" | "rtl"
7
7
 
@@ -14,7 +14,7 @@
14
14
  export let focused = false
15
15
  export let readonly = false
16
16
  export let api
17
- export let startDayOfWeek = "Monday"
17
+ export let startDayOfWeek
18
18
 
19
19
  let isOpen
20
20
  let anchor
@@ -0,0 +1,2 @@
1
+ declare module "*.css"
2
+ declare module "@spectrum-css/*"
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"]