From 6782e65fdf6ea7f79ead3c6907eacf110d097402 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 28 Mar 2023 17:30:11 +0000 Subject: [PATCH] feat: #2 add prompt hints --- app/components/home.module.scss | 58 ++++++++++++++++++++++- app/components/home.tsx | 82 +++++++++++++++++++++++++++++---- app/components/settings.tsx | 41 ++++++++++++++++- app/components/ui-lib.tsx | 2 +- app/icons/edit.svg | 1 + app/locales/cn.ts | 10 ++++ app/store/app.ts | 4 ++ app/store/prompt.ts | 63 +++++++++++++++++-------- package.json | 4 +- yarn.lock | 20 ++++---- 10 files changed, 238 insertions(+), 47 deletions(-) create mode 100644 app/icons/edit.svg diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 730c05ef..462b19ce 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -333,11 +333,65 @@ .chat-input-panel { position: absolute; - bottom: 20px; + bottom: 0px; display: flex; width: 100%; padding: 20px; box-sizing: border-box; + flex-direction: column; +} + +@mixin single-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.prompt-hints { + min-height: 20px; + width: 100%; + max-height: 50vh; + overflow: auto; + display: flex; + flex-direction: column-reverse; + + background-color: var(--white); + border: var(--border-in-light); + border-radius: 10px; + margin-bottom: 10px; + box-shadow: var(--shadow); + + .prompt-hint { + color: var(--black); + padding: 6px 10px; + animation: slide-in ease 0.3s; + cursor: pointer; + transition: all ease 0.3s; + border: transparent 1px solid; + margin: 4px; + border-radius: 8px; + + &:not(:last-child) { + margin-top: 0; + } + + .hint-title { + font-size: 12px; + font-weight: bolder; + + @include single-line(); + } + .hint-content { + font-size: 12px; + + @include single-line(); + } + + &-selected, + &:hover { + border-color: var(--primary); + } + } } .chat-input-panel-inner { @@ -375,7 +429,7 @@ position: absolute; right: 30px; - bottom: 10px; + bottom: 30px; } .export-content { diff --git a/app/components/home.tsx b/app/components/home.tsx index 850c5177..1a84d727 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -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 ( +
+ {props.prompts.map((prompt, i) => ( +
props.onPromptSelect(prompt)} + > +
{prompt.title}
+
{prompt.content}
+
+ ))} +
+ ); +} + 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(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([]); + 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(null); - const inputRef = useRef(null); // wont scroll while hovering messages const [autoScroll, setAutoScroll] = useState(false); @@ -373,17 +429,21 @@ export function Chat(props: { showSideBar?: () => void }) {
+