Skip to content

Commit 51bf84c

Browse files
Add chat history navigation functionality (mckaywrigley#1403)
* Add chat history navigation functionality * Update chat history navigation keybindings --------- Co-authored-by: Mckay Wrigley <[email protected]>
1 parent 360c260 commit 51bf84c

File tree

3 files changed

+127
-25
lines changed

3 files changed

+127
-25
lines changed

app/[locale]/login/page.tsx

+13-6
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,23 @@ export default async function Login({
9696
const email = formData.get("email") as string
9797
const password = formData.get("password") as string
9898

99-
const emailDomainWhitelistPatternsString = await getEnvVarOrEdgeConfigValue("EMAIL_DOMAIN_WHITELIST")
100-
const emailDomainWhitelist = emailDomainWhitelistPatternsString?.trim() ? emailDomainWhitelistPatternsString?.split(",") : [];
101-
const emailWhitelistPatternsString = await getEnvVarOrEdgeConfigValue("EMAIL_WHITELIST")
102-
const emailWhitelist = emailWhitelistPatternsString?.trim() ? emailWhitelistPatternsString?.split(",") : [];
99+
const emailDomainWhitelistPatternsString = await getEnvVarOrEdgeConfigValue(
100+
"EMAIL_DOMAIN_WHITELIST"
101+
)
102+
const emailDomainWhitelist = emailDomainWhitelistPatternsString?.trim()
103+
? emailDomainWhitelistPatternsString?.split(",")
104+
: []
105+
const emailWhitelistPatternsString =
106+
await getEnvVarOrEdgeConfigValue("EMAIL_WHITELIST")
107+
const emailWhitelist = emailWhitelistPatternsString?.trim()
108+
? emailWhitelistPatternsString?.split(",")
109+
: []
103110

104111
// If there are whitelist patterns, check if the email is allowed to sign up
105-
if(emailDomainWhitelist.length > 0 || emailWhitelist.length > 0) {
112+
if (emailDomainWhitelist.length > 0 || emailWhitelist.length > 0) {
106113
const domainMatch = emailDomainWhitelist?.includes(email.split("@")[1])
107114
const emailMatch = emailWhitelist?.includes(email)
108-
if(!domainMatch && !emailMatch) {
115+
if (!domainMatch && !emailMatch) {
109116
return redirect(
110117
`/login?message=Email ${email} is not allowed to sign up.`
111118
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { ChatbotUIContext } from "@/context/context"
2+
import { useContext, useEffect, useState } from "react"
3+
4+
/**
5+
* Custom hook for handling chat history in the chat component.
6+
* It provides functions to set the new message content to the previous or next user message in the chat history.
7+
*
8+
* @returns An object containing the following functions:
9+
* - setNewMessageContentToPreviousUserMessage: Sets the new message content to the previous user message.
10+
* - setNewMessageContentToNextUserMessage: Sets the new message content to the next user message in the chat history.
11+
*/
12+
export const useChatHistoryHandler = () => {
13+
const { setUserInput, chatMessages, isGenerating } =
14+
useContext(ChatbotUIContext)
15+
const userRoleString = "user"
16+
17+
const [messageHistoryIndex, setMessageHistoryIndex] = useState<number>(
18+
chatMessages.length
19+
)
20+
21+
useEffect(() => {
22+
// If messages get deleted the history index pointed could be out of bounds
23+
if (!isGenerating && messageHistoryIndex > chatMessages.length)
24+
setMessageHistoryIndex(chatMessages.length)
25+
}, [chatMessages, isGenerating, messageHistoryIndex])
26+
27+
/**
28+
* Sets the new message content to the previous user message.
29+
*/
30+
const setNewMessageContentToPreviousUserMessage = () => {
31+
let tempIndex = messageHistoryIndex
32+
while (
33+
tempIndex > 0 &&
34+
chatMessages[tempIndex - 1].message.role !== userRoleString
35+
) {
36+
tempIndex--
37+
}
38+
39+
const previousUserMessage =
40+
chatMessages.length > 0 && tempIndex > 0
41+
? chatMessages[tempIndex - 1]
42+
: null
43+
if (previousUserMessage) {
44+
setUserInput(previousUserMessage.message.content)
45+
setMessageHistoryIndex(tempIndex - 1)
46+
}
47+
}
48+
49+
/**
50+
* Sets the new message content to the next user message in the chat history.
51+
* If there is a next user message, it updates the user input and message history index accordingly.
52+
* If there is no next user message, it resets the user input and sets the message history index to the end of the chat history.
53+
*/
54+
const setNewMessageContentToNextUserMessage = () => {
55+
let tempIndex = messageHistoryIndex
56+
while (
57+
tempIndex < chatMessages.length - 1 &&
58+
chatMessages[tempIndex + 1].message.role !== userRoleString
59+
) {
60+
tempIndex++
61+
}
62+
63+
const nextUserMessage =
64+
chatMessages.length > 0 && tempIndex < chatMessages.length - 1
65+
? chatMessages[tempIndex + 1]
66+
: null
67+
setUserInput(nextUserMessage?.message.content || "")
68+
setMessageHistoryIndex(
69+
nextUserMessage ? tempIndex + 1 : chatMessages.length
70+
)
71+
}
72+
73+
return {
74+
setNewMessageContentToPreviousUserMessage,
75+
setNewMessageContentToNextUserMessage
76+
}
77+
}

components/chat/chat-input.tsx

+37-19
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { TextareaAutosize } from "../ui/textarea-autosize"
1616
import { ChatCommandInput } from "./chat-command-input"
1717
import { ChatFilesDisplay } from "./chat-files-display"
1818
import { useChatHandler } from "./chat-hooks/use-chat-handler"
19+
import { useChatHistoryHandler } from "./chat-hooks/use-chat-history"
1920
import { usePromptAndCommand } from "./chat-hooks/use-prompt-and-command"
2021
import { useSelectFileHandler } from "./chat-hooks/use-select-file-handler"
2122

@@ -66,6 +67,11 @@ export const ChatInput: FC<ChatInputProps> = ({}) => {
6667

6768
const { filesToAccept, handleSelectDeviceFile } = useSelectFileHandler()
6869

70+
const {
71+
setNewMessageContentToNextUserMessage,
72+
setNewMessageContentToPreviousUserMessage
73+
} = useChatHistoryHandler()
74+
6975
const fileInputRef = useRef<HTMLInputElement>(null)
7076

7177
useEffect(() => {
@@ -81,34 +87,46 @@ export const ChatInput: FC<ChatInputProps> = ({}) => {
8187
handleSendMessage(userInput, chatMessages, false)
8288
}
8389

90+
// Consolidate conditions to avoid TypeScript error
8491
if (
85-
isPromptPickerOpen &&
86-
(event.key === "Tab" ||
87-
event.key === "ArrowUp" ||
88-
event.key === "ArrowDown")
92+
isPromptPickerOpen ||
93+
isFilePickerOpen ||
94+
isToolPickerOpen ||
95+
isAssistantPickerOpen
8996
) {
97+
if (
98+
event.key === "Tab" ||
99+
event.key === "ArrowUp" ||
100+
event.key === "ArrowDown"
101+
) {
102+
event.preventDefault()
103+
// Toggle focus based on picker type
104+
if (isPromptPickerOpen) setFocusPrompt(!focusPrompt)
105+
if (isFilePickerOpen) setFocusFile(!focusFile)
106+
if (isToolPickerOpen) setFocusTool(!focusTool)
107+
if (isAssistantPickerOpen) setFocusAssistant(!focusAssistant)
108+
}
109+
}
110+
111+
if (event.key === "ArrowUp" && event.shiftKey && event.ctrlKey) {
90112
event.preventDefault()
91-
setFocusPrompt(!focusPrompt)
113+
setNewMessageContentToPreviousUserMessage()
92114
}
93115

94-
if (
95-
isFilePickerOpen &&
96-
(event.key === "Tab" ||
97-
event.key === "ArrowUp" ||
98-
event.key === "ArrowDown")
99-
) {
116+
if (event.key === "ArrowDown" && event.shiftKey && event.ctrlKey) {
100117
event.preventDefault()
101-
setFocusFile(!focusFile)
118+
setNewMessageContentToNextUserMessage()
102119
}
103120

104-
if (
105-
isToolPickerOpen &&
106-
(event.key === "Tab" ||
107-
event.key === "ArrowUp" ||
108-
event.key === "ArrowDown")
109-
) {
121+
//use shift+ctrl+up and shift+ctrl+down to navigate through chat history
122+
if (event.key === "ArrowUp" && event.shiftKey && event.ctrlKey) {
123+
event.preventDefault()
124+
setNewMessageContentToPreviousUserMessage()
125+
}
126+
127+
if (event.key === "ArrowDown" && event.shiftKey && event.ctrlKey) {
110128
event.preventDefault()
111-
setFocusTool(!focusTool)
129+
setNewMessageContentToNextUserMessage()
112130
}
113131

114132
if (

0 commit comments

Comments
 (0)