From 149b416b319cff9fffe671c7c4c6118e4fd88434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E5=9E=92?= Date: Thu, 12 Dec 2024 16:08:31 +0800 Subject: [PATCH] feat: extension update --- entrypoints/background.js | 6 +- entrypoints/content/App.jsx | 3 +- entrypoints/content/components/Modal.jsx | 15 ++++ package.json | 2 +- public/_locales/en/messages.json | 13 ++++ public/_locales/zh/messages.json | 13 ++++ src/components/MarkdownRenderer.jsx | 2 +- src/main.jsx | 4 +- src/pages/chat/components/AiMessage.jsx | 23 ++++--- src/pages/chat/components/ChatInput/index.jsx | 5 +- .../SingleChatPanel/index.jsx | 2 +- .../web-copilot/components/WordExplainer.jsx | 12 ++-- src/pages/web-copilot/index.jsx | 68 ++++++++++++++++--- src/utils/models.js | 3 +- src/utils/utils.js | 6 +- wxt.config.js | 5 +- 16 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 public/_locales/en/messages.json create mode 100644 public/_locales/zh/messages.json diff --git a/entrypoints/background.js b/entrypoints/background.js index 56c9c74..c7c0791 100644 --- a/entrypoints/background.js +++ b/entrypoints/background.js @@ -3,13 +3,13 @@ import { browser } from "wxt/browser"; export default defineBackground(() => { browser.contextMenus.create({ id: "explain", - title: "解释:%s", + title: `${browser.i18n.getMessage("explain")}`, contexts: ["selection"] }); browser.contextMenus.create({ - id: "summarize", - title: "总结全文", + id: "copilot", + title: `${browser.i18n.getMessage("copilot")}`, contexts: ["page"] }); diff --git a/entrypoints/content/App.jsx b/entrypoints/content/App.jsx index 6622c96..7347214 100644 --- a/entrypoints/content/App.jsx +++ b/entrypoints/content/App.jsx @@ -31,9 +31,10 @@ export default ({ ctx }) => { registerContentMessage(async payload => { const { menuItemId, selectionText } = payload; setPayload({ - message: menuItemId === 'explain' ? selectionText : '总结全文', + message: menuItemId === 'explain' ? selectionText : '', type: menuItemId, context: getPageContext(), + from: 'silo:extension', }); setShowPage(true); }); diff --git a/entrypoints/content/components/Modal.jsx b/entrypoints/content/components/Modal.jsx index 7c1c47a..5a6b4e4 100644 --- a/entrypoints/content/components/Modal.jsx +++ b/entrypoints/content/components/Modal.jsx @@ -6,6 +6,21 @@ export default function ({ close, payload, visible }) { const [loaded, setLoaded] = useState(false); const iframeRef = useRef(null); + useEffect(() => { + const listener = event => { + const message = JSON.parse(event.data); + console.log(message); + + if (message.type === 'silo:web-copilot-close') { + close(); + } + }; + window.addEventListener('message', listener); + return () => { + window.removeEventListener('message', listener); + }; + }, []); + useEffect(() => { if (payload.type) { iframeRef.current.contentWindow.postMessage(JSON.stringify(payload), '*'); diff --git a/package.json b/package.json index 30532cf..52afd3a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "silo", "private": true, - "version": "1.6.3", + "version": "1.6.4", "type": "module", "scripts": { "dev": "vite", diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json new file mode 100644 index 0000000..df841a4 --- /dev/null +++ b/public/_locales/en/messages.json @@ -0,0 +1,13 @@ +{ + "extName": { + "message": "Silo - Multi-model chat, text-to-images" + }, + "explain": { + "message": "Explain: %s", + "description": "Explain the selected text" + }, + "copilot": { + "message": "Ask Silo", + "description": "Ask Silo" + } +} \ No newline at end of file diff --git a/public/_locales/zh/messages.json b/public/_locales/zh/messages.json new file mode 100644 index 0000000..5cb9823 --- /dev/null +++ b/public/_locales/zh/messages.json @@ -0,0 +1,13 @@ +{ + "extName": { + "message": "Silo - 多模型对话,文生图" + }, + "explain": { + "message": "解释:%s", + "description": "Explain the selected text" + }, + "copilot": { + "message": "问问 Silo", + "description": "Ask Silo" + } +} \ No newline at end of file diff --git a/src/components/MarkdownRenderer.jsx b/src/components/MarkdownRenderer.jsx index d9c23d3..4f0c945 100644 --- a/src/components/MarkdownRenderer.jsx +++ b/src/components/MarkdownRenderer.jsx @@ -103,7 +103,7 @@ export default function MarkdownRenderer({ content, loading = false }) { children={content} remarkPlugins={[remarkGfm, remarkMath]} rehypePlugins={[rehypeKatex]} - className="silo-markdown prose !max-w-none prose-pre:bg-transparent prose-slate prose-red prose-sm dark:prose-invert prose-headings:text-primary dark:prose-headings:text-[#2ddaff]" + className="silo-markdown prose !max-w-none prose-pre:bg-transparent prose-slate prose-red prose-sm dark:prose-invert prose-headings:text-primary dark:prose-headings:text-[#2ddaff] group" components={{ code(props) { let { children, className, node, ...rest } = props; diff --git a/src/main.jsx b/src/main.jsx index d79467d..7820060 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,14 +1,14 @@ import './utils/exception.js'; -import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App.jsx'; import './index.css'; import './i18n'; import { Analytics } from '@vercel/analytics/react'; +import { isBrowserExtension } from './utils/utils.js'; createRoot(document.getElementById('root')).render( <> - + {!isBrowserExtension ? : null} ); diff --git a/src/pages/chat/components/AiMessage.jsx b/src/pages/chat/components/AiMessage.jsx index d0df19b..7ab623f 100644 --- a/src/pages/chat/components/AiMessage.jsx +++ b/src/pages/chat/components/AiMessage.jsx @@ -80,16 +80,21 @@ export default function AiMessage({ )} -
- message.success(t('common.copied'))} + {!loading && ( +
- - -
+ message.success(t('common.copied'))} + > + + +
+ )} {formattedInfo && (
{formattedInfo}
)} diff --git a/src/pages/chat/components/ChatInput/index.jsx b/src/pages/chat/components/ChatInput/index.jsx index 27071f9..6aaa842 100644 --- a/src/pages/chat/components/ChatInput/index.jsx +++ b/src/pages/chat/components/ChatInput/index.jsx @@ -40,7 +40,10 @@ export default function ({ onStop(true); } else if (action === SHORTCUTS_ACTIONS.STOP) { onStop(false); - } else if (action === SHORTCUTS_ACTIONS.RESEND_LAST) { + } else if ( + action === SHORTCUTS_ACTIONS.RESEND_LAST && + messageHistory.length > 0 + ) { const { message, image, chatId } = messageHistory[messageHistory.length - 1]; removeUserMessage(chatId); diff --git a/src/pages/chat/components/MultiPanelMessages/SingleChatPanel/index.jsx b/src/pages/chat/components/MultiPanelMessages/SingleChatPanel/index.jsx index a503c67..36ea1d6 100644 --- a/src/pages/chat/components/MultiPanelMessages/SingleChatPanel/index.jsx +++ b/src/pages/chat/components/MultiPanelMessages/SingleChatPanel/index.jsx @@ -20,7 +20,7 @@ import { Dropdown } from 'tdesign-react'; import ChatModelSelector from './ChatModelSelector'; import { message } from 'tdesign-react'; import { GUIDE_STEP } from '@src/utils/types'; -export default function ({ model, plain = false }) { +export default function ({ model, plain = false, className = '' }) { const { t } = useTranslation(); const messages = useChatMessages(model); const { activeModels, removeActiveModel } = useActiveModels(); diff --git a/src/pages/web-copilot/components/WordExplainer.jsx b/src/pages/web-copilot/components/WordExplainer.jsx index 4e0a3a2..90522ef 100644 --- a/src/pages/web-copilot/components/WordExplainer.jsx +++ b/src/pages/web-copilot/components/WordExplainer.jsx @@ -41,13 +41,14 @@ export default function ({ context, word }) { ) : `${prompt}\n${context}\n`; - const { loading, onSubmit, onStop } = useSiloChat(systemPrompt, activeModels); + const { loading, onSubmit, onStop, messageHistory } = useSiloChat( + systemPrompt, + activeModels + ); useEffect(() => { if (!isModelInit) return; - console.log(appActiveModels); - onStop(true); if (word) { setTimeout(() => { onSubmit(word); @@ -72,8 +73,6 @@ export default function ({ context, word }) { }); }; - console.log(isModelInit); - if (!isModelInit || !filteredResponses.length) return null; return (
@@ -114,7 +113,7 @@ export default function ({ context, word }) {
onCursor(-1)} @@ -122,6 +121,7 @@ export default function ({ context, word }) { onStop={onStop} onSubmit={onSubmit} loading={filteredResponses[activeIndex]?.loading} + messageHistory={messageHistory} />
diff --git a/src/pages/web-copilot/index.jsx b/src/pages/web-copilot/index.jsx index 041ad53..05e4b55 100644 --- a/src/pages/web-copilot/index.jsx +++ b/src/pages/web-copilot/index.jsx @@ -1,18 +1,26 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import mockData from './mock.json'; import { useEffect } from 'react'; import WordExplainer from './components/WordExplainer'; import { isBrowserExtension } from '@src/utils/utils'; +import WebCopilotSettings from '@src/components/Header/WebCopilotSettingsModal'; +import { useDarkMode } from '@src/utils/use'; + +const commonIconClass = + ' cursor-pointer opacity-50 hover:opacity-100 transition-opacity duration-300 text-xl ml-2'; + export default function () { + const settingsRef = useRef(null); const [message, setMessage] = useState(!isBrowserExtension ? mockData : null); - - console.log(message); + const [isDark, setDarkMode] = useDarkMode(); useEffect(() => { const handleMessage = event => { if (event.data && typeof event.data === 'string') { - console.log(event.data); - setMessage(JSON.parse(event.data)); + const message = JSON.parse(event.data); + if (message.from === 'silo:extension') { + setMessage(message); + } } }; @@ -21,14 +29,52 @@ export default function () { window.removeEventListener('message', handleMessage); }; }, []); + return ( -
- {message && ( - + +
+ logo { + window.open(browser.runtime.getURL('ext.html'), '_blank'); + }} /> - )} + setDarkMode(!isDark)} + > + { + settingsRef.current?.open(); + }} + > + { + window.parent?.postMessage( + JSON.stringify({ type: 'silo:web-copilot-close' }), + '*' + ); + }} + > +
+
+ {message && ( + + )} +
); } diff --git a/src/utils/models.js b/src/utils/models.js index b708a92..fb04a3d 100644 --- a/src/utils/models.js +++ b/src/utils/models.js @@ -154,7 +154,8 @@ const SILICON_MODELS = [ textModelOf("Vendor-A/Qwen/Qwen2-72B-Instruct", 1, 32, false), // textModelOf("nvidia/Llama-3.1-Nemotron-70B-Instruct", 4.13, 32, true), textModelOf("google/gemma-2-27b-it", 1.26, 8, true), - textModelOf("meta-llama/Meta-Llama-3.1-70B-Instruct", 4.13, 8, true), + textModelOf("meta-llama/Llama-3.3-70B-Instruct", 4.13, 32, true), + textModelOf("meta-llama/Meta-Llama-3.1-70B-Instruct", 4.13, 32, true), // textModelOf("meta-llama/Meta-Llama-3-70B-Instruct", 4.13, 8, true), textModelOf("meta-llama/Meta-Llama-3.1-405B-Instruct", 21, 32, true), textModelOf("Qwen/Qwen2-VL-72B-Instruct", 4.13, 32, false, true), diff --git a/src/utils/utils.js b/src/utils/utils.js index f556a60..6736188 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -3,7 +3,6 @@ import { getChatResolver, isLimitedModel } from './models'; import { fmtBaseUrl } from "./format"; import { isExperienceSK } from "@src/store/storage"; import { LOCAL_STORAGE_KEY } from "./types"; -import { getLocalStorage } from "./helpers"; export function createOpenAICompatibleRequestOptions (sk, model, messages, options = {}) { return { method: 'POST', @@ -38,7 +37,10 @@ const defaultModelIdResolver = modelId => { export function openAiCompatibleChat (baseUrl, sk, modelIdResolver, model, messages, chatOptions, controller, onChunk, onEnd, onError) { const modelId = (modelIdResolver || defaultModelIdResolver)(model) // 取出模型ID if (!sk) { - return onError(new Error('API Key未配置')) + if (isBrowserExtension) { + return onError(new Error('Please configure the API key in the extension main page and reload this page')) + } + return onError(new Error('API Key is missing')) } const startTime = Date.now() fetch(`${fmtBaseUrl(baseUrl)}/chat/completions`, { ...createOpenAICompatibleRequestOptions(sk, modelId, messages, chatOptions), signal: controller.current.signal }) diff --git a/wxt.config.js b/wxt.config.js index bb62a68..e6c6f72 100644 --- a/wxt.config.js +++ b/wxt.config.js @@ -5,7 +5,7 @@ console.log(path.resolve(__dirname, './src')); export default defineConfig({ modules: ['@wxt-dev/module-react'], manifest: { - name: 'Silo - Multi-model chat, text-to-image', + name: '__MSG_extName__', permissions: ['contextMenus'], action: {}, "web_accessible_resources": [ @@ -13,7 +13,8 @@ export default defineConfig({ "resources": ["ext.html"], "matches": ['*://*/*'] } - ] + ], + default_locale: 'en', }, vite: () => ({ esbuild: {