mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-08-08 18:57:07 +08:00
feat: #2 add prompt hints
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { IconButton } from "./button";
|
||||
import styles from "./home.module.scss";
|
||||
@@ -28,6 +29,7 @@ import Locale from "../locales";
|
||||
import dynamic from "next/dynamic";
|
||||
import { REPO_URL } from "../constant";
|
||||
import { ControllerPool } from "../requests";
|
||||
import { Prompt, usePromptStore } from "../store/prompt";
|
||||
|
||||
export function Loading(props: { noLogo?: boolean }) {
|
||||
return (
|
||||
@@ -146,24 +148,77 @@ function useSubmitHandler() {
|
||||
};
|
||||
}
|
||||
|
||||
export function PromptHints(props: {
|
||||
prompts: Prompt[];
|
||||
onPromptSelect: (prompt: Prompt) => void;
|
||||
}) {
|
||||
if (props.prompts.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className={styles["prompt-hints"]}>
|
||||
{props.prompts.map((prompt, i) => (
|
||||
<div
|
||||
className={styles["prompt-hint"]}
|
||||
key={prompt.title + i.toString()}
|
||||
onClick={() => props.onPromptSelect(prompt)}
|
||||
>
|
||||
<div className={styles["hint-title"]}>{prompt.title}</div>
|
||||
<div className={styles["hint-content"]}>{prompt.content}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Chat(props: { showSideBar?: () => void }) {
|
||||
type RenderMessage = Message & { preview?: boolean };
|
||||
|
||||
const chatStore = useChatStore();
|
||||
const [session, sessionIndex] = useChatStore((state) => [
|
||||
state.currentSession(),
|
||||
state.currentSessionIndex,
|
||||
]);
|
||||
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [userInput, setUserInput] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { submitKey, shouldSubmit } = useSubmitHandler();
|
||||
|
||||
const onUserInput = useChatStore((state) => state.onUserInput);
|
||||
// prompt hints
|
||||
const promptStore = usePromptStore();
|
||||
const [promptHints, setPromptHints] = useState<Prompt[]>([]);
|
||||
const onSearch = useDebouncedCallback(
|
||||
(text: string) => {
|
||||
if (chatStore.config.disablePromptHint) return;
|
||||
setPromptHints(promptStore.search(text));
|
||||
},
|
||||
100,
|
||||
{ leading: true, trailing: true }
|
||||
);
|
||||
|
||||
const onPromptSelect = (prompt: Prompt) => {
|
||||
setUserInput(prompt.content);
|
||||
setPromptHints([]);
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
// only search prompts when user input is short
|
||||
const SEARCH_TEXT_LIMIT = 10;
|
||||
const onInput = (text: string) => {
|
||||
setUserInput(text);
|
||||
const n = text.trim().length;
|
||||
if (n === 0 || n > SEARCH_TEXT_LIMIT) {
|
||||
setPromptHints([]);
|
||||
} else {
|
||||
onSearch(text);
|
||||
}
|
||||
};
|
||||
|
||||
// submit user input
|
||||
const onUserSubmit = () => {
|
||||
if (userInput.length <= 0) return;
|
||||
setIsLoading(true);
|
||||
onUserInput(userInput).then(() => setIsLoading(false));
|
||||
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
||||
setUserInput("");
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
@@ -198,7 +253,9 @@ export function Chat(props: { showSideBar?: () => void }) {
|
||||
for (let i = botIndex; i >= 0; i -= 1) {
|
||||
if (messages[i].role === "user") {
|
||||
setIsLoading(true);
|
||||
onUserInput(messages[i].content).then(() => setIsLoading(false));
|
||||
chatStore
|
||||
.onUserInput(messages[i].content)
|
||||
.then(() => setIsLoading(false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -206,7 +263,6 @@ export function Chat(props: { showSideBar?: () => void }) {
|
||||
|
||||
// for auto-scroll
|
||||
const latestMessageRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// wont scroll while hovering messages
|
||||
const [autoScroll, setAutoScroll] = useState(false);
|
||||
@@ -373,17 +429,21 @@ export function Chat(props: { showSideBar?: () => void }) {
|
||||
</div>
|
||||
|
||||
<div className={styles["chat-input-panel"]}>
|
||||
<PromptHints prompts={promptHints} onPromptSelect={onPromptSelect} />
|
||||
<div className={styles["chat-input-panel-inner"]}>
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
className={styles["chat-input"]}
|
||||
placeholder={Locale.Chat.Input(submitKey)}
|
||||
rows={3}
|
||||
onInput={(e) => setUserInput(e.currentTarget.value)}
|
||||
rows={4}
|
||||
onInput={(e) => onInput(e.currentTarget.value)}
|
||||
value={userInput}
|
||||
onKeyDown={(e) => onInputKeyDown(e as any)}
|
||||
onFocus={() => setAutoScroll(true)}
|
||||
onBlur={() => setAutoScroll(false)}
|
||||
onBlur={() => {
|
||||
setAutoScroll(false);
|
||||
setTimeout(() => setPromptHints([]), 100);
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
<IconButton
|
||||
@@ -411,9 +471,11 @@ function useSwitchTheme() {
|
||||
document.body.classList.add("light");
|
||||
}
|
||||
|
||||
const themeColor = getComputedStyle(document.body).getPropertyValue("--theme-color").trim();
|
||||
const themeColor = getComputedStyle(document.body)
|
||||
.getPropertyValue("--theme-color")
|
||||
.trim();
|
||||
const metaDescription = document.querySelector('meta[name="theme-color"]');
|
||||
metaDescription?.setAttribute('content', themeColor);
|
||||
metaDescription?.setAttribute("content", themeColor);
|
||||
}, [config.theme]);
|
||||
}
|
||||
|
||||
@@ -566,7 +628,7 @@ export function Home() {
|
||||
<IconButton
|
||||
icon={<AddIcon />}
|
||||
text={Locale.Home.NewChat}
|
||||
onClick={()=>{
|
||||
onClick={() => {
|
||||
createNewSession();
|
||||
setShowSideBar(false);
|
||||
}}
|
||||
|
Reference in New Issue
Block a user