diff --git a/README.md b/README.md index 877507cc..99391c43 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ ```bash cd chat; cd api +go install github.com/cosmtrek/air@latest go mod tidy # export env var, change base on your env export PG_HOST=192.168.0.135 diff --git a/web/src/assets/recommend.json b/web/src/assets/recommend.json new file mode 100644 index 00000000..71f62a6b --- /dev/null +++ b/web/src/assets/recommend.json @@ -0,0 +1,8 @@ +[ + { + "key": "awesome-chatgpt-prompts-zh", + "desc": "ChatGPT 中文调教指南", + "downloadUrl": "https://raw.githubusercontent.com/Nothing1024/chatgpt-prompt-collection/main/awesome-chatgpt-prompts-zh.json", + "url": "https://github.com/PlexPt/awesome-chatgpt-prompts-zh" + } +] \ No newline at end of file diff --git a/web/src/components/common/PromptStore/index.vue b/web/src/components/common/PromptStore/index.vue new file mode 100644 index 00000000..f2a8eee1 --- /dev/null +++ b/web/src/components/common/PromptStore/index.vue @@ -0,0 +1,429 @@ + + + \ No newline at end of file diff --git a/web/src/components/common/index.ts b/web/src/components/common/index.ts index 16b6d108..d8f03ec6 100644 --- a/web/src/components/common/index.ts +++ b/web/src/components/common/index.ts @@ -3,5 +3,6 @@ import NaiveProvider from './NaiveProvider/index.vue' import SvgIcon from './SvgIcon/index.vue' import UserAvatar from './UserAvatar/index.vue' import Setting from './Setting/index.vue' +import PromptStore from './PromptStore/index.vue' -export { HoverButton, NaiveProvider, SvgIcon, UserAvatar, Setting } +export { HoverButton, NaiveProvider, SvgIcon, UserAvatar, Setting, PromptStore } diff --git a/web/src/locales/en-US.json b/web/src/locales/en-US.json index 5bdfb591..4212bf0d 100644 --- a/web/src/locales/en-US.json +++ b/web/src/locales/en-US.json @@ -75,7 +75,7 @@ "maxTokens": "Max Total Tokens : {maxTokens}", "model": "Model (OpenAI GPT4 needs separate application, Claude key can use all Claude models)", "new": "New Chat", - "placeholder": "What would you like to say... (Shift + Enter = newline)", + "placeholder": "What would you like to say... (Shift + Enter = newline, '/' to trigger prompts)", "placeholderMobile": "What would you like to say...", "presencePenalty": "Presence Penalty", "sessionConfig": "Conversation Settings:", diff --git a/web/src/locales/zh-CN.json b/web/src/locales/zh-CN.json index 1b2485c2..086e6011 100644 --- a/web/src/locales/zh-CN.json +++ b/web/src/locales/zh-CN.json @@ -34,7 +34,7 @@ }, "chat": { "new": "新对话", - "placeholder": "来说点什么吧...(Shift + Enter = 换行)", + "placeholder": "来说点什么吧...(Shift + Enter = 换行, '/' 触发提示词)", "placeholderMobile": "来说点什么...", "copy": "复制", "copied": "复制成功", diff --git a/web/src/locales/zh-TW.json b/web/src/locales/zh-TW.json index 7a081bb4..08029fbd 100644 --- a/web/src/locales/zh-TW.json +++ b/web/src/locales/zh-TW.json @@ -75,7 +75,7 @@ "maxTokens": "最大問答總token數量: {maxTokens}", "model": "模型(OPENAI GPT4 需要個別申請,CLAUDE的免費密鑰可以使用所有CLAUDE模型)", "new": "新建聊天", - "placeholder": "說些什麼...(Shift + Enter = 換行)", + "placeholder": "說些什麼...(Shift + Enter = 換行, '/' 触发提示词)", "placeholderMobile": "說些什麼...", "presencePenalty": "存在懲罰", "sessionConfig": "會話配置", diff --git a/web/src/store/modules/index.ts b/web/src/store/modules/index.ts index b33b51b5..c6ff33c6 100644 --- a/web/src/store/modules/index.ts +++ b/web/src/store/modules/index.ts @@ -2,3 +2,4 @@ export * from './app' export * from './chat' export * from './user' export * from './auth' +export * from './prompt' diff --git a/web/src/store/modules/prompt/helper.ts b/web/src/store/modules/prompt/helper.ts new file mode 100644 index 00000000..973438d3 --- /dev/null +++ b/web/src/store/modules/prompt/helper.ts @@ -0,0 +1,18 @@ +import { ss } from '@/utils/storage' + +const LOCAL_NAME = 'promptStore' + +export type PromptList = [] + +export interface PromptStore { + promptList: PromptList +} + +export function getLocalPromptList(): PromptStore { + const promptStore: PromptStore | undefined = ss.get(LOCAL_NAME) + return promptStore ?? { promptList: [] } +} + +export function setLocalPromptList(promptStore: PromptStore): void { + ss.set(LOCAL_NAME, promptStore) +} \ No newline at end of file diff --git a/web/src/store/modules/prompt/index.ts b/web/src/store/modules/prompt/index.ts new file mode 100644 index 00000000..869400f3 --- /dev/null +++ b/web/src/store/modules/prompt/index.ts @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' +import type { PromptStore } from './helper' +import { getLocalPromptList, setLocalPromptList } from './helper' + +export const usePromptStore = defineStore('prompt-store', { + state: (): PromptStore => getLocalPromptList(), + + actions: { + updatePromptList(promptList: []) { + this.$patch({ promptList }) + setLocalPromptList({ promptList }) + }, + getPromptList() { + return this.$state + }, + }, +}) \ No newline at end of file diff --git a/web/src/views/chat/index.vue b/web/src/views/chat/index.vue index 1d030a6b..d72eadaf 100644 --- a/web/src/views/chat/index.vue +++ b/web/src/views/chat/index.vue @@ -2,7 +2,9 @@ import { computed, onMounted, onUnmounted, ref } from 'vue' import { v4 as uuidv4 } from 'uuid' import { useRoute } from 'vue-router' -import { NButton, NInput, NModal, useDialog, useMessage } from 'naive-ui' +import { NModal } from 'naive-ui' +import { storeToRefs } from 'pinia' +import { NAutoComplete, NButton, NInput, useDialog, useMessage } from 'naive-ui' import html2canvas from 'html2canvas' import { Message } from './components' import { useScroll } from './hooks/useScroll' @@ -13,7 +15,7 @@ import SessionConfig from './components/Session/SessionConfig.vue' import { createChatSnapshot, fetchChatStream, updateChatData } from '@/api' import { HoverButton, SvgIcon } from '@/components/common' import { useBasicLayout } from '@/hooks/useBasicLayout' -import { useChatStore } from '@/store' +import { useChatStore,usePromptStore } from '@/store' import { t } from '@/locales' import { genTempDownloadLink } from '@/utils/download' let controller = new AbortController() @@ -38,8 +40,40 @@ const chatSession = computed(() => chatStore.getChatSessionByUuid(uuid)) const prompt = ref('') const loading = ref(false) + const showModal = ref(false) +// 添加PromptStore +const promptStore = usePromptStore() +// 使用storeToRefs,保证store修改后,联想部分能够重新渲染 +const { promptList: promptTemplate } = storeToRefs(promptStore) + + +// 可优化部分 +// 搜索选项计算,这里使用value作为索引项,所以当出现重复value时渲染异常(多项同时出现选中效果) +// 理想状态下其实应该是key作为索引项,但官方的renderOption会出现问题,所以就需要value反renderLabel实现 +const searchOptions = computed(() => { + if (prompt.value.startsWith('/')) { + return promptTemplate.value.filter((item: { key: string }) => item.key.toLowerCase().includes(prompt.value.substring(1).toLowerCase())).map((obj: { value: any }) => { + return { + label: obj.value, + value: obj.value, + } + }) + } + else { + return [] + } +}) +// value反渲染key +const renderOption = (option: { label: string }) => { + for (const i of promptTemplate.value) { + if (i.value === option.label) + return [i.key] + } + return [] +} + function handleSubmit() { onConversationStream() } @@ -510,10 +544,15 @@ function getDataFromResponseText(responseText: string): string { - + + + import type { CSSProperties } from 'vue' -import { computed, watch } from 'vue' +import { computed, watch, ref } from 'vue' import { NButton, NLayoutSider } from 'naive-ui' import List from './List.vue' @@ -10,11 +10,13 @@ import { useBasicLayout } from '@/hooks/useBasicLayout' import { t } from '@/locales' import { SvgIcon } from '@/components/common' import { getChatSessionDefault } from '@/api' +import { PromptStore } from '@/components/common' const appStore = useAppStore() const chatStore = useChatStore() const { isMobile } = useBasicLayout() +const show = ref(false) const collapsed = computed(() => appStore.siderCollapsed) @@ -88,6 +90,11 @@ watch(
+
+ + Prompt Store + +