forked from XiaoMo/ChatGPT-Next-Web
feat: #2 add prompt hints
This commit is contained in:
parent
7d5e742ea6
commit
6782e65fdf
@ -333,11 +333,65 @@
|
|||||||
|
|
||||||
.chat-input-panel {
|
.chat-input-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 20px;
|
bottom: 0px;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
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 {
|
.chat-input-panel-inner {
|
||||||
@ -375,7 +429,7 @@
|
|||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 30px;
|
right: 30px;
|
||||||
bottom: 10px;
|
bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-content {
|
.export-content {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
||||||
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import styles from "./home.module.scss";
|
import styles from "./home.module.scss";
|
||||||
@ -28,6 +29,7 @@ import Locale from "../locales";
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { REPO_URL } from "../constant";
|
import { REPO_URL } from "../constant";
|
||||||
import { ControllerPool } from "../requests";
|
import { ControllerPool } from "../requests";
|
||||||
|
import { Prompt, usePromptStore } from "../store/prompt";
|
||||||
|
|
||||||
export function Loading(props: { noLogo?: boolean }) {
|
export function Loading(props: { noLogo?: boolean }) {
|
||||||
return (
|
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 }) {
|
export function Chat(props: { showSideBar?: () => void }) {
|
||||||
type RenderMessage = Message & { preview?: boolean };
|
type RenderMessage = Message & { preview?: boolean };
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
const [session, sessionIndex] = useChatStore((state) => [
|
const [session, sessionIndex] = useChatStore((state) => [
|
||||||
state.currentSession(),
|
state.currentSession(),
|
||||||
state.currentSessionIndex,
|
state.currentSessionIndex,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const [userInput, setUserInput] = useState("");
|
const [userInput, setUserInput] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { submitKey, shouldSubmit } = useSubmitHandler();
|
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
|
// submit user input
|
||||||
const onUserSubmit = () => {
|
const onUserSubmit = () => {
|
||||||
if (userInput.length <= 0) return;
|
if (userInput.length <= 0) return;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
onUserInput(userInput).then(() => setIsLoading(false));
|
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
};
|
};
|
||||||
@ -198,7 +253,9 @@ export function Chat(props: { showSideBar?: () => void }) {
|
|||||||
for (let i = botIndex; i >= 0; i -= 1) {
|
for (let i = botIndex; i >= 0; i -= 1) {
|
||||||
if (messages[i].role === "user") {
|
if (messages[i].role === "user") {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
onUserInput(messages[i].content).then(() => setIsLoading(false));
|
chatStore
|
||||||
|
.onUserInput(messages[i].content)
|
||||||
|
.then(() => setIsLoading(false));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +263,6 @@ export function Chat(props: { showSideBar?: () => void }) {
|
|||||||
|
|
||||||
// for auto-scroll
|
// for auto-scroll
|
||||||
const latestMessageRef = useRef<HTMLDivElement>(null);
|
const latestMessageRef = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
||||||
|
|
||||||
// wont scroll while hovering messages
|
// wont scroll while hovering messages
|
||||||
const [autoScroll, setAutoScroll] = useState(false);
|
const [autoScroll, setAutoScroll] = useState(false);
|
||||||
@ -373,17 +429,21 @@ export function Chat(props: { showSideBar?: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles["chat-input-panel"]}>
|
<div className={styles["chat-input-panel"]}>
|
||||||
|
<PromptHints prompts={promptHints} onPromptSelect={onPromptSelect} />
|
||||||
<div className={styles["chat-input-panel-inner"]}>
|
<div className={styles["chat-input-panel-inner"]}>
|
||||||
<textarea
|
<textarea
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className={styles["chat-input"]}
|
className={styles["chat-input"]}
|
||||||
placeholder={Locale.Chat.Input(submitKey)}
|
placeholder={Locale.Chat.Input(submitKey)}
|
||||||
rows={3}
|
rows={4}
|
||||||
onInput={(e) => setUserInput(e.currentTarget.value)}
|
onInput={(e) => onInput(e.currentTarget.value)}
|
||||||
value={userInput}
|
value={userInput}
|
||||||
onKeyDown={(e) => onInputKeyDown(e as any)}
|
onKeyDown={(e) => onInputKeyDown(e as any)}
|
||||||
onFocus={() => setAutoScroll(true)}
|
onFocus={() => setAutoScroll(true)}
|
||||||
onBlur={() => setAutoScroll(false)}
|
onBlur={() => {
|
||||||
|
setAutoScroll(false);
|
||||||
|
setTimeout(() => setPromptHints([]), 100);
|
||||||
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -411,9 +471,11 @@ function useSwitchTheme() {
|
|||||||
document.body.classList.add("light");
|
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"]');
|
const metaDescription = document.querySelector('meta[name="theme-color"]');
|
||||||
metaDescription?.setAttribute('content', themeColor);
|
metaDescription?.setAttribute("content", themeColor);
|
||||||
}, [config.theme]);
|
}, [config.theme]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@ import styles from "./settings.module.scss";
|
|||||||
import ResetIcon from "../icons/reload.svg";
|
import ResetIcon from "../icons/reload.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
import CloseIcon from "../icons/close.svg";
|
||||||
import ClearIcon from "../icons/clear.svg";
|
import ClearIcon from "../icons/clear.svg";
|
||||||
|
import EditIcon from "../icons/edit.svg";
|
||||||
|
|
||||||
import { List, ListItem, Popover } from "./ui-lib";
|
import { List, ListItem, Popover, showToast } from "./ui-lib";
|
||||||
|
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import {
|
import {
|
||||||
@ -19,12 +20,13 @@ import {
|
|||||||
useUpdateStore,
|
useUpdateStore,
|
||||||
useAccessStore,
|
useAccessStore,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
import { Avatar } from "./home";
|
import { Avatar, PromptHints } from "./home";
|
||||||
|
|
||||||
import Locale, { changeLang, getLang } from "../locales";
|
import Locale, { changeLang, getLang } from "../locales";
|
||||||
import { getCurrentCommitId } from "../utils";
|
import { getCurrentCommitId } from "../utils";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { UPDATE_URL } from "../constant";
|
import { UPDATE_URL } from "../constant";
|
||||||
|
import { SearchService, usePromptStore } from "../store/prompt";
|
||||||
|
|
||||||
function SettingItem(props: {
|
function SettingItem(props: {
|
||||||
title: string;
|
title: string;
|
||||||
@ -78,6 +80,10 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const promptStore = usePromptStore();
|
||||||
|
const builtinCount = SearchService.count.builtin;
|
||||||
|
const customCount = promptStore.prompts.size ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles["window-header"]}>
|
<div className={styles["window-header"]}>
|
||||||
@ -242,6 +248,37 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
</SettingItem>
|
</SettingItem>
|
||||||
</div>
|
</div>
|
||||||
</List>
|
</List>
|
||||||
|
<List>
|
||||||
|
<SettingItem
|
||||||
|
title={Locale.Settings.Prompt.Disable.Title}
|
||||||
|
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={config.disablePromptHint}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.disablePromptHint = e.currentTarget.checked)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
title={Locale.Settings.Prompt.List}
|
||||||
|
subTitle={Locale.Settings.Prompt.ListCount(
|
||||||
|
builtinCount,
|
||||||
|
customCount
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
icon={<EditIcon />}
|
||||||
|
text={Locale.Settings.Prompt.Edit}
|
||||||
|
onClick={() => showToast(Locale.WIP)}
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
</List>
|
||||||
<List>
|
<List>
|
||||||
{enabledAccessControl ? (
|
{enabledAccessControl ? (
|
||||||
<SettingItem
|
<SettingItem
|
||||||
|
@ -36,7 +36,7 @@ export function ListItem(props: { children: JSX.Element[] }) {
|
|||||||
return <div className={styles["list-item"]}>{props.children}</div>;
|
return <div className={styles["list-item"]}>{props.children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function List(props: { children: JSX.Element[] }) {
|
export function List(props: { children: JSX.Element[] | JSX.Element }) {
|
||||||
return <div className={styles.list}>{props.children}</div>;
|
return <div className={styles.list}>{props.children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
app/icons/edit.svg
Normal file
1
app/icons/edit.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(10.5 11) rotate(0 1.4166666666666665 1.8333333333333333)" d="M2.83,0L2.83,3C2.83,3.37 2.53,3.67 2.17,3.67L0,3.67 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(2.6666666666666665 1.3333333333333333) rotate(0 5.333333333333333 6.666666666666666)" d="M10.67,4L10.67,0.67C10.67,0.3 10.37,0 10,0L0.67,0C0.3,0 0,0.3 0,0.67L0,12.67C0,13.03 0.3,13.33 0.67,13.33L2.67,13.33 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.333333333333333 5.333333333333333) rotate(0 2.333333333333333 0)" d="M0,0L4.67,0 " /><path id="路径 4" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(7.666666666666666 7.666666666666666) rotate(0 2.833333333333333 3.5)" d="M0,7L5.67,0 " /><path id="路径 5" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.333333333333333 8) rotate(0 1.3333333333333333 0)" d="M0,0L2.67,0 " /></g></g></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -62,6 +62,16 @@ const cn = {
|
|||||||
SendKey: "发送键",
|
SendKey: "发送键",
|
||||||
Theme: "主题",
|
Theme: "主题",
|
||||||
TightBorder: "紧凑边框",
|
TightBorder: "紧凑边框",
|
||||||
|
Prompt: {
|
||||||
|
Disable: {
|
||||||
|
Title: "禁用提示词自动补全",
|
||||||
|
SubTitle: "禁用后将无法自动根据输入补全",
|
||||||
|
},
|
||||||
|
List: "自定义提示词列表",
|
||||||
|
ListCount: (builtin: number, custom: number) =>
|
||||||
|
`内置 ${builtin} 条,用户定义 ${custom} 条`,
|
||||||
|
Edit: "编辑",
|
||||||
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "附带历史消息数",
|
Title: "附带历史消息数",
|
||||||
SubTitle: "每次请求携带的历史消息数",
|
SubTitle: "每次请求携带的历史消息数",
|
||||||
|
@ -40,6 +40,8 @@ export interface ChatConfig {
|
|||||||
theme: Theme;
|
theme: Theme;
|
||||||
tightBorder: boolean;
|
tightBorder: boolean;
|
||||||
|
|
||||||
|
disablePromptHint: boolean;
|
||||||
|
|
||||||
modelConfig: {
|
modelConfig: {
|
||||||
model: string;
|
model: string;
|
||||||
temperature: number;
|
temperature: number;
|
||||||
@ -124,6 +126,8 @@ const DEFAULT_CONFIG: ChatConfig = {
|
|||||||
theme: Theme.Auto as Theme,
|
theme: Theme.Auto as Theme,
|
||||||
tightBorder: false,
|
tightBorder: false,
|
||||||
|
|
||||||
|
disablePromptHint: false,
|
||||||
|
|
||||||
modelConfig: {
|
modelConfig: {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import JsSearch from "js-search";
|
import Fuse from "fuse.js";
|
||||||
|
import { showToast } from "../components/ui-lib";
|
||||||
|
|
||||||
export interface Prompt {
|
export interface Prompt {
|
||||||
id?: number;
|
id?: number;
|
||||||
shortcut: string;
|
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
@ -22,36 +22,30 @@ export const PROMPT_KEY = "prompt-store";
|
|||||||
|
|
||||||
export const SearchService = {
|
export const SearchService = {
|
||||||
ready: false,
|
ready: false,
|
||||||
progress: 0, // 0 - 1, 1 means ready
|
engine: new Fuse<Prompt>([], { keys: ["title"] }),
|
||||||
engine: new JsSearch.Search("prompts"),
|
count: {
|
||||||
deleted: new Set<number>(),
|
builtin: 0,
|
||||||
|
},
|
||||||
|
|
||||||
async init(prompts: PromptStore["prompts"]) {
|
init(prompts: Prompt[]) {
|
||||||
this.engine.addIndex("id");
|
if (this.ready) {
|
||||||
this.engine.addIndex("shortcut");
|
return;
|
||||||
this.engine.addIndex("title");
|
|
||||||
|
|
||||||
const n = prompts.size;
|
|
||||||
let count = 0;
|
|
||||||
for await (const prompt of prompts.values()) {
|
|
||||||
this.engine.addDocument(prompt);
|
|
||||||
count += 1;
|
|
||||||
this.progress = count / n;
|
|
||||||
}
|
}
|
||||||
|
this.engine.setCollection(prompts);
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
remove(id: number) {
|
remove(id: number) {
|
||||||
this.deleted.add(id);
|
this.engine.remove((doc) => doc.id === id);
|
||||||
},
|
},
|
||||||
|
|
||||||
add(prompt: Prompt) {
|
add(prompt: Prompt) {
|
||||||
this.engine.addDocument(prompt);
|
this.engine.add(prompt);
|
||||||
},
|
},
|
||||||
|
|
||||||
search(text: string) {
|
search(text: string) {
|
||||||
const results = this.engine.search(text) as Prompt[];
|
const results = this.engine.search(text);
|
||||||
return results.filter((v) => !v.id || !this.deleted.has(v.id));
|
return results.map((v) => v.item);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,6 +85,35 @@ export const usePromptStore = create<PromptStore>()(
|
|||||||
{
|
{
|
||||||
name: PROMPT_KEY,
|
name: PROMPT_KEY,
|
||||||
version: 1,
|
version: 1,
|
||||||
|
onRehydrateStorage(state) {
|
||||||
|
const PROMPT_URL = "./prompts.json";
|
||||||
|
|
||||||
|
type PromptList = Array<[string, string]>;
|
||||||
|
|
||||||
|
fetch(PROMPT_URL)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
const builtinPrompts = [res.en, res.cn]
|
||||||
|
.map((promptList: PromptList) => {
|
||||||
|
return promptList.map(
|
||||||
|
([title, content]) =>
|
||||||
|
({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
} as Prompt)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.concat([...(state?.prompts?.values() ?? [])]);
|
||||||
|
|
||||||
|
const allPromptsForSearch = builtinPrompts.reduce(
|
||||||
|
(pre, cur) => pre.concat(cur),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
SearchService.count.builtin = res.en.length + res.cn.length;
|
||||||
|
SearchService.init(allPromptsForSearch);
|
||||||
|
showToast(`已加载 ${allPromptsForSearch.length} 条 Prompts`);
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
"@types/js-search": "^1.4.0",
|
|
||||||
"@types/node": "^18.14.6",
|
"@types/node": "^18.14.6",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
@ -24,7 +23,7 @@
|
|||||||
"eslint": "8.35.0",
|
"eslint": "8.35.0",
|
||||||
"eslint-config-next": "13.2.3",
|
"eslint-config-next": "13.2.3",
|
||||||
"eventsource-parser": "^0.1.0",
|
"eventsource-parser": "^0.1.0",
|
||||||
"js-search": "^2.0.0",
|
"fuse.js": "^6.6.2",
|
||||||
"next": "^13.2.3",
|
"next": "^13.2.3",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.1",
|
||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
@ -38,6 +37,7 @@
|
|||||||
"sass": "^1.59.2",
|
"sass": "^1.59.2",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
|
"use-debounce": "^9.0.3",
|
||||||
"zustand": "^4.3.6"
|
"zustand": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
yarn.lock
20
yarn.lock
@ -1320,11 +1320,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "*"
|
"@types/unist" "*"
|
||||||
|
|
||||||
"@types/js-search@^1.4.0":
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/js-search/-/js-search-1.4.0.tgz#f2d4afa176a4fc7b17fb46a1593847887fa1fb7b"
|
|
||||||
integrity sha512-OMDWvQP2AmxpQI9vFh7U/TzExNGB9Sj9WQCoxUR8VXZEv6jM4cyNzLODkh1gkBHJ9Er7kdasChzEpba4FxLGaA==
|
|
||||||
|
|
||||||
"@types/json5@^0.0.29":
|
"@types/json5@^0.0.29":
|
||||||
version "0.0.29"
|
version "0.0.29"
|
||||||
resolved "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
@ -2524,6 +2519,11 @@ functions-have-names@^1.2.2:
|
|||||||
resolved "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
resolved "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||||
|
|
||||||
|
fuse.js@^6.6.2:
|
||||||
|
version "6.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.6.2.tgz#fe463fed4b98c0226ac3da2856a415576dc9a111"
|
||||||
|
integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==
|
||||||
|
|
||||||
gensync@^1.0.0-beta.2:
|
gensync@^1.0.0-beta.2:
|
||||||
version "1.0.0-beta.2"
|
version "1.0.0-beta.2"
|
||||||
resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||||
@ -3042,11 +3042,6 @@ js-sdsl@^4.1.4:
|
|||||||
resolved "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
|
resolved "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
|
||||||
integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
|
integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
|
||||||
|
|
||||||
js-search@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-search/-/js-search-2.0.0.tgz#84dc9d44e34ca0870d067e04b86d8038b77edc26"
|
|
||||||
integrity sha512-lJ8KzjlwcelIWuAdKyzsXv45W6OIwRpayzc7XmU8mzgWadg5UVOKVmnq/tXudddEB9Ceic3tVaGu6QOK/eebhg==
|
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
@ -4660,6 +4655,11 @@ uri-js@^4.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
|
use-debounce@^9.0.3:
|
||||||
|
version "9.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.3.tgz#bac660c19ab7b38662e08608fee23c7ad303f532"
|
||||||
|
integrity sha512-FhtlbDtDXILJV7Lix5OZj5yX/fW1tzq+VrvK1fnT2bUrPOGruU9Rw8NCEn+UI9wopfERBEZAOQ8lfeCJPllgnw==
|
||||||
|
|
||||||
use-sync-external-store@1.2.0:
|
use-sync-external-store@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
resolved "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
|
Loading…
Reference in New Issue
Block a user