refactor: #121 trigger auto-cmp with / prefix

This commit is contained in:
Yifei Zhang 2023-03-29 15:31:55 +00:00
parent 73b2ede53a
commit e606810581
4 changed files with 64 additions and 47 deletions

View File

@ -102,7 +102,7 @@ export function ChatList() {
state.currentSessionIndex, state.currentSessionIndex,
state.selectSession, state.selectSession,
state.removeSession, state.removeSession,
] ],
); );
return ( return (
@ -128,7 +128,7 @@ function useSubmitHandler() {
const shouldSubmit = (e: KeyboardEvent) => { const shouldSubmit = (e: KeyboardEvent) => {
if (e.key !== "Enter") return false; if (e.key !== "Enter") return false;
return ( return (
(config.submitKey === SubmitKey.AltEnter && e.altKey) || (config.submitKey === SubmitKey.AltEnter && e.altKey) ||
(config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) || (config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
@ -170,7 +170,10 @@ export function PromptHints(props: {
); );
} }
export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean }) { export function Chat(props: {
showSideBar?: () => void;
sideBarShowing?: boolean;
}) {
type RenderMessage = Message & { preview?: boolean }; type RenderMessage = Message & { preview?: boolean };
const chatStore = useChatStore(); const chatStore = useChatStore();
@ -190,11 +193,10 @@ export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean
const [promptHints, setPromptHints] = useState<Prompt[]>([]); const [promptHints, setPromptHints] = useState<Prompt[]>([]);
const onSearch = useDebouncedCallback( const onSearch = useDebouncedCallback(
(text: string) => { (text: string) => {
if (chatStore.config.disablePromptHint) return;
setPromptHints(promptStore.search(text)); setPromptHints(promptStore.search(text));
}, },
100, 100,
{ leading: true, trailing: true } { leading: true, trailing: true },
); );
const onPromptSelect = (prompt: Prompt) => { const onPromptSelect = (prompt: Prompt) => {
@ -203,20 +205,31 @@ export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean
inputRef.current?.focus(); inputRef.current?.focus();
}; };
const scrollInput = () => {
const dom = inputRef.current;
if (!dom) return;
const paddingBottomNum: number = parseInt(
window.getComputedStyle(dom).paddingBottom,
10,
);
dom.scrollTop = dom.scrollHeight - dom.offsetHeight + paddingBottomNum;
};
// only search prompts when user input is short // only search prompts when user input is short
const SEARCH_TEXT_LIMIT = 30; const SEARCH_TEXT_LIMIT = 30;
const onInput = (text: string) => { const onInput = (text: string) => {
const textareaDom = inputRef.current scrollInput();
if (textareaDom) {
const paddingBottomNum: number = parseInt(window.getComputedStyle(textareaDom).paddingBottom, 10);
textareaDom.scrollTop = textareaDom.scrollHeight - textareaDom.offsetHeight + paddingBottomNum;
}
setUserInput(text); setUserInput(text);
const n = text.trim().length; const n = text.trim().length;
if (n === 0 || n > SEARCH_TEXT_LIMIT) {
// clear search results
if (n === 0) {
setPromptHints([]); setPromptHints([]);
} else { } else if (!chatStore.config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
onSearch(text); // check if need to trigger auto completion
if (text.startsWith("/") && text.length > 1) {
onSearch(text.slice(1));
}
} }
}; };
@ -285,7 +298,7 @@ export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean
preview: true, preview: true,
}, },
] ]
: [] : [],
) )
.concat( .concat(
userInput.length > 0 userInput.length > 0
@ -297,7 +310,7 @@ export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean
preview: true, preview: true,
}, },
] ]
: [] : [],
); );
// auto scroll // auto scroll
@ -380,32 +393,33 @@ export function Chat(props: { showSideBar?: () => void, sideBarShowing?: boolean
</div> </div>
)} )}
<div className={styles["chat-message-item"]}> <div className={styles["chat-message-item"]}>
{(!isUser && !(message.preview || message.content.length === 0)) && ( {!isUser &&
<div className={styles["chat-message-top-actions"]}> !(message.preview || message.content.length === 0) && (
{message.streaming ? ( <div className={styles["chat-message-top-actions"]}>
<div {message.streaming ? (
className={styles["chat-message-top-action"]} <div
onClick={() => onUserStop(i)} className={styles["chat-message-top-action"]}
> onClick={() => onUserStop(i)}
{Locale.Chat.Actions.Stop} >
</div> {Locale.Chat.Actions.Stop}
) : ( </div>
<div ) : (
className={styles["chat-message-top-action"]} <div
onClick={() => onResend(i)} className={styles["chat-message-top-action"]}
> onClick={() => onResend(i)}
{Locale.Chat.Actions.Retry} >
</div> {Locale.Chat.Actions.Retry}
)} </div>
)}
<div <div
className={styles["chat-message-top-action"]} className={styles["chat-message-top-action"]}
onClick={() => copyToClipboard(message.content)} onClick={() => copyToClipboard(message.content)}
> >
{Locale.Chat.Actions.Copy} {Locale.Chat.Actions.Copy}
</div>
</div> </div>
</div> )}
)}
{(message.preview || message.content.length === 0) && {(message.preview || message.content.length === 0) &&
!isUser ? ( !isUser ? (
<LoadingIcon /> <LoadingIcon />
@ -560,7 +574,7 @@ export function Home() {
state.newSession, state.newSession,
state.currentSessionIndex, state.currentSessionIndex,
state.removeSession, state.removeSession,
] ],
); );
const loading = !useHasHydrated(); const loading = !useHasHydrated();
const [showSideBar, setShowSideBar] = useState(true); const [showSideBar, setShowSideBar] = useState(true);
@ -653,7 +667,11 @@ export function Home() {
}} }}
/> />
) : ( ) : (
<Chat key="chat" showSideBar={() => setShowSideBar(true)} sideBarShowing={showSideBar} /> <Chat
key="chat"
showSideBar={() => setShowSideBar(true)}
sideBarShowing={showSideBar}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -77,7 +77,7 @@ const cn = {
Prompt: { Prompt: {
Disable: { Disable: {
Title: "禁用提示词自动补全", Title: "禁用提示词自动补全",
SubTitle: "禁用后将无法自动根据输入补全", SubTitle: "在输入框开头输入 / 即可触发自动补全",
}, },
List: "自定义提示词列表", List: "自定义提示词列表",
ListCount: (builtin: number, custom: number) => ListCount: (builtin: number, custom: number) =>

View File

@ -79,7 +79,7 @@ const en: LocaleType = {
Prompt: { Prompt: {
Disable: { Disable: {
Title: "Disable auto-completion", Title: "Disable auto-completion",
SubTitle: "After disabling, auto-completion will not be available", SubTitle: "Input / to trigger auto-completion",
}, },
List: "Prompt List", List: "Prompt List",
ListCount: (builtin: number, custom: number) => ListCount: (builtin: number, custom: number) =>

View File

@ -20,7 +20,7 @@ const tw: LocaleType = {
Retry: "重試", Retry: "重試",
}, },
Typing: "正在輸入…", Typing: "正在輸入…",
Input: (submitKey: string) => { Input: (submitKey: string) => {
var inputHints = `輸入訊息後,按下 ${submitKey} 鍵即可發送`; var inputHints = `輸入訊息後,按下 ${submitKey} 鍵即可發送`;
if (submitKey === String(SubmitKey.Enter)) { if (submitKey === String(SubmitKey.Enter)) {
inputHints += "Shift + Enter 鍵換行"; inputHints += "Shift + Enter 鍵換行";
@ -78,7 +78,7 @@ const tw: LocaleType = {
Prompt: { Prompt: {
Disable: { Disable: {
Title: "停用提示詞自動補全", Title: "停用提示詞自動補全",
SubTitle: "若停用後,將無法自動根據輸入進行補全", SubTitle: "在輸入框開頭輸入 / 即可觸發自動補全",
}, },
List: "自定義提示詞列表", List: "自定義提示詞列表",
ListCount: (builtin: number, custom: number) => ListCount: (builtin: number, custom: number) =>
@ -124,8 +124,7 @@ const tw: LocaleType = {
Prompt: { Prompt: {
History: (content: string) => History: (content: string) =>
"這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content, "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content,
Topic: Topic: "直接返回這句話的簡要主題,無須解釋,若無主題,請直接返回「閒聊」",
"直接返回這句話的簡要主題,無須解釋,若無主題,請直接返回「閒聊」",
Summarize: Summarize:
"簡要總結一下你和用戶的對話,作為後續的上下文提示 prompt且字數控制在 50 字以內", "簡要總結一下你和用戶的對話,作為後續的上下文提示 prompt且字數控制在 50 字以內",
}, },