@budibase/frontend-core 3.41.2 → 3.42.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.2",
3
+ "version": "v3.42.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": "556bd76bac5c556b307b1022fce8e1fe51d928d7"
26
+ "gitHead": "180c04c6852aa7acf0402d53dc909eab764d0251"
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(
@@ -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,
@@ -59,6 +60,8 @@
59
60
  isAgentPreviewChat?: boolean
60
61
  readOnly?: boolean
61
62
  readOnlyReason?: "disabled" | "deleted" | "offline"
63
+ promptHistory?: string[]
64
+ onpromptsubmitted?: (prompt: string) => void
62
65
  }
63
66
 
64
67
  let {
@@ -75,6 +78,8 @@
75
78
  isAgentPreviewChat = false,
76
79
  readOnly = false,
77
80
  readOnlyReason,
81
+ promptHistory = [],
82
+ onpromptsubmitted,
78
83
  }: Props = $props()
79
84
 
80
85
  // Per-escalation in-flight flag + the message relayed from resolve, so the
@@ -133,6 +138,7 @@
133
138
  let expandedTools = $state<Record<string, boolean>>({})
134
139
  let reasoningTextByMessageId = $state<Record<string, string>>({})
135
140
  let inputValue = $state("")
141
+ let promptHistoryIndex = $state<number | undefined>()
136
142
  let lastInitialPrompt = $state("")
137
143
  let isPreparingResponse = $state(false)
138
144
  const resetPendingResponse = () => {
@@ -560,6 +566,23 @@
560
566
  return
561
567
  }
562
568
 
569
+ if (isAgentPreviewChat) {
570
+ const navigationState = navigatePromptHistory({
571
+ key: event.key,
572
+ history: promptHistory,
573
+ inputValue,
574
+ index: promptHistoryIndex,
575
+ })
576
+ if (navigationState) {
577
+ event.preventDefault()
578
+ inputValue = navigationState.inputValue
579
+ promptHistoryIndex = navigationState.index
580
+ await tick()
581
+ textareaElement?.setSelectionRange(inputValue.length, inputValue.length)
582
+ return
583
+ }
584
+ }
585
+
563
586
  if (event.key === "Enter" && !event.shiftKey) {
564
587
  event.preventDefault()
565
588
  await sendMessage()
@@ -634,6 +657,8 @@
634
657
  }
635
658
 
636
659
  inputValue = ""
660
+ promptHistoryIndex = undefined
661
+ onpromptsubmitted?.(text)
637
662
  chatInstance.sendMessage({ text })
638
663
  isPreparingResponse = false
639
664
  }
@@ -955,6 +980,7 @@
955
980
  bind:this={textareaElement}
956
981
  class="input spectrum-Textfield-input"
957
982
  onkeydown={handleKeyDown}
983
+ oninput={() => (promptHistoryIndex = undefined)}
958
984
  placeholder="Ask..."
959
985
  disabled={isRequestPending}
960
986
  ></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
+ }
@@ -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"]