├── .eslintrc ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── README ├── AGI.png ├── AGI_EN.png ├── Character.png ├── Character_EN.png ├── Model.png ├── Model_EN.png ├── basic node.png └── wechat.png ├── README_EN.md ├── examples ├── combo │ ├── Earth_1687744307436.json │ ├── Generate PPT_4c35df6f7513b1ed1926b9b7b1415ab3.json │ ├── sd图像生成API_4c35df6f7513b1ed1926b9b7b1415ab3.json │ ├── test-用户输入-文案_DG-E7g-s_AMnSkk0W-nHe.json │ ├── 提取邮件信息_2f77d6ca8eaec9534233812874e6f202.json │ ├── 漫画生成_hxTElLdYiylK18tU5cPtf.json │ ├── 知识星球助手_mFnsueSPO86CkHudr6sD2.json │ └── 输入网站后跳转_3T2K8wncaKIuerL5rAxpW.json ├── demo01.mp4 ├── demo01.png └── demo02.mp4 ├── package-lock.json ├── package.json ├── public ├── DeepL.svg ├── GPT.png ├── Tour.png ├── Web.svg ├── all.svg ├── avatars │ └── Designer.png ├── background.png ├── bing.svg ├── chatdoc.png ├── chatgpt-icon.png ├── chatgpt.png ├── google.svg ├── icon-128.png ├── icon-34.png ├── logo.png ├── logo.svg ├── perplexity-ai.webp ├── removebg.png └── semanticscholar.png ├── scripts └── build.ts ├── src ├── assets │ └── img │ │ └── logo.svg ├── components │ ├── ChatbotMain.tsx │ ├── Setup.tsx │ ├── Style.tsx │ ├── Template.tsx │ ├── Utils.tsx │ ├── background │ │ ├── Agent.js │ │ ├── ChatBot.js │ │ ├── ChatGPT.js │ │ ├── Common.js │ │ ├── Credit.js │ │ ├── Infmonkeys.js │ │ └── NewBing.js │ ├── buttons │ │ ├── CloseButton.tsx │ │ ├── CopyButton.tsx │ │ ├── DownSquare.tsx │ │ ├── DownloadButton.tsx │ │ ├── FullscreenButton.tsx │ │ ├── HelpButton.tsx │ │ ├── ImportOfficialCombo.tsx │ │ ├── OpenFileButton.tsx │ │ └── SetupButton.tsx │ ├── chatbot │ │ ├── ChatBotConfig.tsx │ │ ├── ChatBotInput.tsx │ │ ├── ChatBotMiniCard.tsx │ │ ├── ChatBotPanel.tsx │ │ ├── ChatBotSelect.tsx │ │ └── ChatBotTalks.tsx │ ├── combo │ │ ├── Agent.tsx │ │ ├── ComboEditor.tsx │ │ └── Prompt.tsx │ ├── files │ │ ├── PDF.tsx │ │ └── PPT.tsx │ ├── flow │ │ ├── Sidebar │ │ │ └── index.tsx │ │ ├── Workflow.tsx │ │ ├── edges │ │ │ └── BWEdge.tsx │ │ ├── index.tsx │ │ ├── nodeComponents │ │ │ ├── APINode.tsx │ │ │ ├── Base.tsx │ │ │ ├── CustomPromptNode.tsx │ │ │ ├── FilePPTCreateNode.tsx │ │ │ ├── InputMergeNode.tsx │ │ │ ├── PromptNode.tsx │ │ │ ├── QueryClickNode.tsx │ │ │ ├── QueryDefaultNode.tsx │ │ │ ├── QueryInputNode.tsx │ │ │ ├── QueryReadNode.tsx │ │ │ ├── RoleNode.tsx │ │ │ ├── UserInputTextNode.tsx │ │ │ └── index.tsx │ │ └── store.ts │ └── runtime │ │ ├── api.tsx │ │ ├── llm.tsx │ │ └── webAgent.tsx ├── config │ ├── app.json │ ├── commonsConfig.json │ ├── editableConfig.json │ └── selectionConfig.json ├── global.d.ts ├── locales │ ├── en.json │ ├── i18nConfig.ts │ └── zh.json ├── manifest │ ├── index.mts │ ├── v2-type.mts │ ├── v2.mts │ ├── v3-type.mts │ └── v3.mts └── pages │ ├── background │ └── index.js │ ├── content │ └── index.tsx │ ├── options │ ├── index.html │ └── index.tsx │ └── popup │ ├── Popup.tsx │ ├── index.html │ └── index.tsx └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended", 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | }, 17 | "ecmaVersion": "latest", 18 | "sourceType": "module" 19 | }, 20 | "plugins": ["react", "@typescript-eslint"], 21 | "rules": { 22 | "react/react-in-jsx-scope": "off" 23 | }, 24 | "globals": { 25 | "chrome": "readonly" 26 | }, 27 | "ignorePatterns": ["watch.js", "dist/**"] 28 | } 29 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.13.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AGIUI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

让AI工作流改变我们的工作方式

4 | 5 | [简体中文](README.md) | [English](README_EN.md) 6 | 7 |
8 | 9 | # Earth 10 | AGI-UI(Artificial General Intelligence user interface,通用人工智能用户界面)组织的使命是改善AGI在PC、Web、Mobile、XR、机器人等领域的人机协作体验,让更多人可以实现自己的AGI交互界面。 11 | 12 | 作为AGIUI的首个开源项目,Earth是一款浏览器插件,暂时支持Chrome和Edge浏览器。如果你感兴趣可以分享我们的项目到各个平台。 13 | 14 | Earth PC&Mac版本:https://github.com/AGIUI/Solis 15 | 16 | [![Twitter](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2FAGIUI%2FEarth)](https://twitter.com/intent/tweet?text=Check%20out%20this%20amazing%20project%20-%20Earth!%20https://github.com/AGIUI/Earth) 17 | [![Weibo](https://img.shields.io/badge/Weibo-Share-red?style=social&logo=weibo)](http://service.weibo.com/share/share.php?url=https%3A%2F%2Fgithub.com%2FAGIUI%2FEarth&title=分享一下AI工作流-Earth%21) 18 | 19 | ## 主要特色 20 | 1. 支持多种AI模型,包括ChatGPT、BingChat和本地LLM(OpenAI官方标准)。 21 | 2. 可以在任何网页上使用,包括搜索引擎、社交媒体、新闻网站等。 22 | 3. 定义自己的工作流,解放双手,提高工作效率。 23 | 4. 可以读取和操控网页信息 24 | 25 | ## 如何使用Earth 26 | ### 1.下载Earth 27 | 插件下载地址1:[chrome Web Store](https://chrome.google.com/webstore/detail/earth/mcbhfeopeloibcjmdljfheanikfhmmgk?hl=zh-CN&authuser=0) 28 | 29 | 插件下载地址2:https://github.com/AGIUI/Earth/releases/ 30 | 31 | 32 | ### 2.Earth安装方法 33 | #### Chrome 34 | 35 | 1. 打开Chrome浏览器,地址栏输入 chrome://extensions/, 勾择开发者模式,点击'加载已解压的扩展程序' 36 | 2. 选择Earth文件夹,点击确定。扩展程序列表出现你导入的扩展程序即为成功。 37 | 38 | #### Edge 39 | 40 | 1. 打开Edge浏览器,地址栏输入 edge://extensions/, 勾择开发者模式,点击'加载已解压的扩展程序' 41 | 2. 选择Earth文件夹,点击确定。扩展程序列表出现你导入的扩展程序即为成功。 42 | 43 | ### 3.设置Bing Chat 44 | 45 | 在浏览器打开[https://www.bing.com/](https://www.bing.com/) ,登录Bing账号(前提是已获得Bing Chat使用权限) 46 | 47 | ### 4.设置API Key和前缀 48 | 49 | 打开插件,点击设置按钮,输入你的API Key和API前缀,点击“更新状态”。 50 | 51 | 52 | 53 | ## 开发者快速上手 54 | 55 | ```sh 56 | # Install packages 57 | npm install 58 | 59 | # Live Dev for multiple browsers 60 | npm run start [browser] 61 | # npm run start chrome firefox safari 62 | 63 | # Build for multiple browsers 64 | npm run build [browser] 65 | # npm run build chrome firefox safari 66 | ``` 67 | 68 | ## 配置数据 69 | 70 | - config.json 里配置你的数据 71 | 72 | 73 | ## Combo的一些示例 74 | [示例1:漫画生成](/examples/combo/%E6%BC%AB%E7%94%BB%E7%94%9F%E6%88%90_hxTElLdYiylK18tU5cPtf.json) 75 | 76 | [示例2:sd图像生成API](/examples/combo/sd%E5%9B%BE%E5%83%8F%E7%94%9F%E6%88%90API_4c35df6f7513b1ed1926b9b7b1415ab3.json) 77 | 78 | [示例3:提取邮件信息](/examples/combo/%E6%8F%90%E5%8F%96%E9%82%AE%E4%BB%B6%E4%BF%A1%E6%81%AF_2f77d6ca8eaec9534233812874e6f202.json) 79 | 80 | [示例4:知识星球助手](/examples/combo/%E7%9F%A5%E8%AF%86%E6%98%9F%E7%90%83%E5%8A%A9%E6%89%8B_mFnsueSPO86CkHudr6sD2.json) 81 | 82 | 83 | ## combo的数据示例 84 | ``` 85 | [ 86 | { 87 | "tag": "提取邮件信息", 88 | "role": { 89 | "text": "麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。" 90 | }, 91 | "combo": 3, 92 | "interfaces": [ 93 | "showInChat", 94 | "contextMenus", 95 | "home" 96 | ], 97 | "isInfinite": false, 98 | "owner": "user", 99 | "prompt": { 100 | "id": "prompt22f77d6ca8eaec9534233812874e6f202", 101 | "nextId": "prompt32f77d6ca8eaec9534233812874e6f202", 102 | "nodeInputId": "", 103 | "role": { 104 | "text": "麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。" 105 | }, 106 | "text": "", 107 | "url": "", 108 | "queryObj": { 109 | "action": "default", 110 | "content": "bindCurrentPage", 111 | }, 112 | "temperature": 0.6, 113 | "model": "ChatGPT", 114 | "input": "nodeInput", 115 | "translate": "default", 116 | "output": "default", 117 | "type": "queryRead" 118 | }, 119 | "version": "0.1.0", 120 | "app": "brainwave", 121 | "id": "2f77d6ca8eaec9534233812874e6f202", 122 | "createDate": 1686640915993, 123 | "prompt2": { 124 | "id": "prompt32f77d6ca8eaec9534233812874e6f202", 125 | "nextId": "prompt42f77d6ca8eaec9534233812874e6f202", 126 | "nodeInputId": "prompt22f77d6ca8eaec9534233812874e6f202", 127 | "role": { 128 | "text": "麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。" 129 | }, 130 | "text": "用中文回答,提取摘要,时间、地点、人物、事件、链接等信息。", 131 | "temperature": 0.3, 132 | "model": "ChatGPT", 133 | "input": "nodeInput", 134 | "userInput": "", 135 | "translate": "translate-zh", 136 | "output": "markdown", 137 | "type": "prompt" 138 | }, 139 | "prompt3": { 140 | "id": "prompt42f77d6ca8eaec9534233812874e6f202", 141 | "nextId": "", 142 | "nodeInputId": "prompt32f77d6ca8eaec9534233812874e6f202", 143 | "role": { 144 | "text": "麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。" 145 | }, 146 | "text": "提取AI相关的信息,给这些信息评分,满分是5分,并写一句推荐文案推荐给我这个内容。输出格式:\n分数、AI信息、推荐文案", 147 | "temperature": 0.24, 148 | "model": "ChatGPT", 149 | "input": "nodeInput", 150 | "translate": "translate-zh", 151 | "output": "default", 152 | "type": "prompt" 153 | } 154 | } 155 | ] 156 | ``` 157 | 158 | ## 版本记录 159 | 160 | #### v0.3.7 161 | - 新增节点:用户输入、合并输入 162 | 163 | #### v0.3.6 164 | - 角色节点的升级,对话界面可以切换角色 165 | 166 | - 提示工程支持嵌入变量的方式 167 | 168 | 169 | #### v0.3.5 170 | - 多国语言支持 i18n 171 | 172 | - 提供若干combo示例 173 | 174 | - 支持创建PPT 175 | 176 | 177 | #### v0.3.4 178 | - 增加整体调试功能 179 | 180 | - 调试窗口可以被收起和显示 181 | 182 | - 允许自由连线 183 | 184 | - 节点实现 185 | 186 | 187 | #### v0.3.3 188 | - 去除newtab 189 | 190 | - 裁剪文本长度的优化(感谢[@nem035/gpt-3-encoder](https://www.npmjs.com/package/@nem035/gpt-3-encoder)) 191 | 192 | - 集成 [brainwave v0.1](https://github.com/AGIUI/BrainWave) 193 | 194 | - 右键菜单功能的完善 195 | 196 | #### v0.3.2 197 | - 增加右键总结、选中内容交互 198 | 199 | - 优化对话框视觉 200 | 201 | - 解决prompt下载的bug 202 | 203 | - 已支持本地LLM的接入(暂时只支持OpenAI官方标准接口形式) 204 | 205 | #### v0.3.1 206 | - combo数据结构调整及编辑器,新增:interfaces(home、contextMenus、showInChat)、input、output 207 | 208 | - combo支持单个导出 209 | 210 | - API节点 211 | 212 | #### v0.2.0 213 | - 新增绑定当前网页、输出格式,combo编辑器导入导出,提供示例 214 | 215 | 216 | ## 未来要做的事情 217 | - AGI实现 218 | ![AGI内部](/README/AGI.png) 219 | ![角色内部](/README/Character.png) 220 | ![模型内部](/README/Model.png) 221 | - 完善基础节点 222 | ![基础节点](/README/basic%20node.png) 223 | - 实现PDF节点:读取、创建、阅读器 224 | 225 | - 实现PPT节点:读取、创建 226 | 227 | - 高亮网页信息节点 228 | - ...... 229 | 230 | 231 | ## 社区 232 | [Discord](https://discord.gg/7YVVhEQExu) 233 | 234 | [问题反馈](https://discord.gg/VtRmJCb8wh) 235 | 236 | ## 联系我们 237 | #### Twitter 238 | [@shadow](https://twitter.com/mixlabPro)、[@薛志荣](https://twitter.com/XueZhirong) 239 | 240 | #### 微信 241 | ![微信](/README/wechat.png) 242 | 243 | ## 相关资料 244 | 245 | [AGIUI 研讨会](https://m.bilibili.com/video/BV1a24y1P7Qg) 246 | 247 | [视频demo](https://space.bilibili.com/540054369) 248 | 249 | [如何构建属于自己的知识引擎?](https://mp.weixin.qq.com/s/W6wjg8873gNci2vcZhamGg) 250 | 251 | [人工智能写作指南v1.0](https://mp.weixin.qq.com/s/sisxObPri8ElG2krgE7w_A) 252 | 253 | [趋势:自主思考,通用人工智能的雏形#生成式智能体](https://mp.weixin.qq.com/s/uMvX_SgWyRpekWIfPpwYCQ) 254 | 255 | [Next Thing:角色+模型+流程+接口调用](https://mp.weixin.qq.com/s/RGcGGsjOF3li_56Cy4myIQ) 256 | 257 | [推荐系统的可解释性到底需不需要?可解释性的UI应该是什么样的?](https://mp.weixin.qq.com/s/HEGrrTkIyY_4EaBpFYJJ7Q) 258 | 259 | [mix-copilot](http://www.mix-copilot.com) 260 | 261 | 262 | [![Star History Chart](https://api.star-history.com/svg?repos=AGIUI/Earth&type=Date)](https://star-history.com/#AGIUI/Earth&Date) 263 | 264 | ## Licence 265 | 此代码在 MIT 许可证下分发。 请参阅此目录中的[许可证](https://github.com/AGIUI/Earth/blob/main/LICENSE)。 266 | -------------------------------------------------------------------------------- /README/AGI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/AGI.png -------------------------------------------------------------------------------- /README/AGI_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/AGI_EN.png -------------------------------------------------------------------------------- /README/Character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/Character.png -------------------------------------------------------------------------------- /README/Character_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/Character_EN.png -------------------------------------------------------------------------------- /README/Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/Model.png -------------------------------------------------------------------------------- /README/Model_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/Model_EN.png -------------------------------------------------------------------------------- /README/basic node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/basic node.png -------------------------------------------------------------------------------- /README/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/README/wechat.png -------------------------------------------------------------------------------- /examples/combo/sd图像生成API_4c35df6f7513b1ed1926b9b7b1415ab3.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "tag": "sd图像生成API", 3 | "role": { 4 | "name": "", 5 | "text": "擅长艺术风格" 6 | }, 7 | "combo": 5, 8 | "interfaces": [ 9 | "showInChat", 10 | "contextMenus" 11 | ], 12 | "isInfinite": false, 13 | "owner": "user", 14 | "prompt": { 15 | "id": "prompt24c35df6f7513b1ed1926b9b7b1415ab3", 16 | "nextId": "prompt34c35df6f7513b1ed1926b9b7b1415ab3", 17 | "nodeInputId": "", 18 | "role": { 19 | "name": "", 20 | "text": "擅长艺术风格" 21 | }, 22 | "text": "", 23 | "url": "", 24 | "queryObj": { 25 | "action": "read", 26 | "content": "bindCurrentPage", 27 | "isQuery": false, 28 | "protocol": "https://", 29 | "query": "", 30 | "url": "" 31 | }, 32 | "temperature": 0.6, 33 | "model": "ChatGPT", 34 | "input": "nodeInput", 35 | "userInput": "", 36 | "translate": "default", 37 | "output": "default", 38 | "type": "queryRead" 39 | }, 40 | "version": "0.1.0", 41 | "app": "brainwave", 42 | "id": "4c35df6f7513b1ed1926b9b7b1415ab3", 43 | "createDate": 1686641022719, 44 | "prompt2": { 45 | "id": "prompt34c35df6f7513b1ed1926b9b7b1415ab3", 46 | "nextId": "prompt44c35df6f7513b1ed1926b9b7b1415ab3", 47 | "nodeInputId": "prompt24c35df6f7513b1ed1926b9b7b1415ab3", 48 | "role": { 49 | "name": "", 50 | "text": "擅长艺术风格" 51 | }, 52 | "text": "提取text里的主题关键词,添加镜头语言,并生成5句英文prompt,配上中英文解释。", 53 | "url": "", 54 | "temperature": 0.3, 55 | "model": "ChatGPT", 56 | "input": "nodeInput", 57 | "userInput": "", 58 | "translate": "translate-en", 59 | "output": "default", 60 | "type": "prompt" 61 | }, 62 | "prompt3": { 63 | "id": "prompt44c35df6f7513b1ed1926b9b7b1415ab3", 64 | "nextId": "prompt54c35df6f7513b1ed1926b9b7b1415ab3", 65 | "nodeInputId": "prompt34c35df6f7513b1ed1926b9b7b1415ab3", 66 | "role": { 67 | "name": "", 68 | "text": "擅长艺术风格" 69 | }, 70 | "text": "挑选出1句最有画面感的英文prompt,并按照格式:\na photo of {human} ,{action} ,{sky},{light},{style} 改写句子,输出结果", 71 | "url": "", 72 | "temperature": 0.6, 73 | "model": "ChatGPT", 74 | "input": "nodeInput", 75 | "userInput": "", 76 | "translate": "translate-en", 77 | "output": "default", 78 | "type": "prompt" 79 | }, 80 | "prompt4": { 81 | "id": "prompt54c35df6f7513b1ed1926b9b7b1415ab3", 82 | "nextId": "prompt64c35df6f7513b1ed1926b9b7b1415ab3", 83 | "nodeInputId": "prompt44c35df6f7513b1ed1926b9b7b1415ab3", 84 | "role": { 85 | "name": "", 86 | "text": "擅长艺术风格" 87 | }, 88 | "text": "只保留英文,提取改写后的结果", 89 | "url": "", 90 | "temperature": 0.6, 91 | "model": "ChatGPT", 92 | "input": "nodeInput", 93 | "userInput": "", 94 | "translate": "translate-en", 95 | "output": "default", 96 | "type": "prompt" 97 | }, 98 | "prompt5": { 99 | "id": "prompt64c35df6f7513b1ed1926b9b7b1415ab3", 100 | "nextId": "", 101 | "nodeInputId": "prompt54c35df6f7513b1ed1926b9b7b1415ab3", 102 | "role": { 103 | "name": "", 104 | "text": "擅长艺术风格" 105 | }, 106 | "text": "", 107 | "url": "127.0.0.1:7860/sdapi/v1/txt2img", 108 | "api": { 109 | "body": { 110 | "batchCount": 3, 111 | "prompt": "${context}", 112 | "steps": 22 113 | }, 114 | "init": { 115 | "body": { 116 | "batch_size": 3, 117 | "prompt": "${context}", 118 | "steps": 22 119 | }, 120 | "cache": "default", 121 | "extract": { 122 | "key": "images", 123 | "type": "images" 124 | }, 125 | "headers": { 126 | "Content-Type": "application/json" 127 | }, 128 | "method": "POST", 129 | "mode": "cors", 130 | "responseExtract": { 131 | "key": "images", 132 | "type": "images" 133 | }, 134 | "responseType": "json" 135 | }, 136 | "isApi": true, 137 | "protocol": "http://", 138 | "responseType": "json", 139 | "url": "127.0.0.1:7860/sdapi/v1/txt2img" 140 | }, 141 | "temperature": 0.6, 142 | "model": "ChatGPT", 143 | "input": "nodeInput", 144 | "userInput": "", 145 | "translate": "default", 146 | "output": "default", 147 | "type": "api" 148 | } 149 | }] -------------------------------------------------------------------------------- /examples/combo/test-用户输入-文案_DG-E7g-s_AMnSkk0W-nHe.json: -------------------------------------------------------------------------------- 1 | [{"tag":"test-用户输入-文案","role":{"name":"","text":"你是一位古诗词高手,会写诗"},"combo":2,"interfaces":["showInChat"],"isInfinite":false,"owner":"user","prompt":{"id":"USERINPUTTEXT_NVZIEYQ3H-TUWY0DRF9QM","type":"userInputText","inputs":{"nodes":[],"output":""},"userInput":"请输入性别","role":{"name":"","text":"你是一位古诗词高手,会写诗"}},"version":"0.3.0","app":"brainwave","id":"DG-E7g-s_AMnSkk0W-nHe","createDate":1688373247275,"prompt2":{"id":"PROMPT_BFCEJTGXUMV74I8TJCMQR","nextId":"","nodeInputId":"USERINPUTTEXT_NVZIEYQ3H-TUWY0DRF9QM","role":{"name":"","text":"你是一位古诗词高手,会写诗"},"text":"结合```${context}``` 写一个古诗词,并写成笑话","url":"","temperature":0.84,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"prompt","merged":null}}] -------------------------------------------------------------------------------- /examples/combo/提取邮件信息_2f77d6ca8eaec9534233812874e6f202.json: -------------------------------------------------------------------------------- 1 | [{"tag":"提取邮件信息","role":{"text":"麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。"},"combo":3,"interfaces":["showInChat","contextMenus","home"],"isInfinite":false,"owner":"user","prompt":{"id":"prompt22f77d6ca8eaec9534233812874e6f202","nextId":"prompt32f77d6ca8eaec9534233812874e6f202","nodeInputId":"","role":{"text":"麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。"},"text":"","url":"","queryObj":{"action":"default","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":"","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryRead"},"version":"0.1.0","app":"brainwave","id":"2f77d6ca8eaec9534233812874e6f202","createDate":1686640915993,"prompt2":{"id":"prompt32f77d6ca8eaec9534233812874e6f202","nextId":"prompt42f77d6ca8eaec9534233812874e6f202","nodeInputId":"prompt22f77d6ca8eaec9534233812874e6f202","role":{"text":"麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。"},"text":"用中文回答,提取摘要,时间、地点、人物、事件、链接等信息。","url":"","temperature":0.3,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-zh","output":"markdown","type":"prompt"},"prompt3":{"id":"prompt42f77d6ca8eaec9534233812874e6f202","nextId":"","nodeInputId":"prompt32f77d6ca8eaec9534233812874e6f202","role":{"text":"麦肯锡咨询专家,掌握了非常多的方法,擅长分析整理信息。"},"text":"提取AI相关的信息,给这些信息评分,满分是5分,并写一句推荐文案推荐给我这个内容。输出格式:\n分数、AI信息、推荐文案","url":"","temperature":0.24,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-zh","output":"default","type":"prompt"}}] -------------------------------------------------------------------------------- /examples/combo/漫画生成_hxTElLdYiylK18tU5cPtf.json: -------------------------------------------------------------------------------- 1 | [{"tag":"漫画生成","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"combo":13,"interfaces":["contextMenus"],"isInfinite":false,"owner":"user","prompt":{"id":"prompt2hxTElLdYiylK18tU5cPtf","nextId":"prompt3hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"read","content":"bindCurrentPageTitle","isQuery":false,"protocol":"https://","query":"","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryRead"},"version":"0.1.0","app":"brainwave","id":"hxTElLdYiylK18tU5cPtf","createDate":1686641008471,"prompt2":{"id":"prompt3hxTElLdYiylK18tU5cPtf","nextId":"prompt4hxTElLdYiylK18tU5cPtf","nodeInputId":"prompt2hxTElLdYiylK18tU5cPtf","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"结合text的内容,编一个科幻故事,并配上中英文解释。","url":"","temperature":0.48,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-en","output":"default","type":"prompt"},"prompt3":{"id":"prompt4hxTElLdYiylK18tU5cPtf","nextId":"prompt5hxTElLdYiylK18tU5cPtf","nodeInputId":"prompt3hxTElLdYiylK18tU5cPtf","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"提取10个关键词,用于描述场景,结合这句模板:a photo of ,关键词 + long view,motion,canno,blender,3d,render,写一句新的","url":"","temperature":0.38,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-en","output":"default","type":"prompt"},"prompt4":{"id":"prompt5hxTElLdYiylK18tU5cPtf","nextId":"prompt6hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"default","content":"bindCurrentPage","delay":5000,"isQuery":false,"protocol":"http://","query":"","url":"localhost:7860/"},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryDefault"},"prompt5":{"id":"prompt6hxTElLdYiylK18tU5cPtf","nextId":"prompt7hxTElLdYiylK18tU5cPtf","nodeInputId":"prompt4hxTElLdYiylK18tU5cPtf","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"input","content":"","isQuery":false,"protocol":"https://","query":"#txt2img_prompt textarea","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryInput"},"prompt6":{"id":"prompt7hxTElLdYiylK18tU5cPtf","nextId":"prompt8hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"click","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":"#txt2img_generate","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryClick"},"prompt7":{"id":"prompt8hxTElLdYiylK18tU5cPtf","nextId":"prompt9hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"default","content":"bindCurrentPage","delay":10000,"isQuery":false,"protocol":"https://","query":"","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryDefault"},"prompt8":{"id":"prompt9hxTElLdYiylK18tU5cPtf","nextId":"prompt10hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"read","content":"bindCurrentPageImages","isQuery":false,"protocol":"https://","query":".thumbnail-item img","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryRead"},"prompt9":{"id":"prompt10hxTElLdYiylK18tU5cPtf","nextId":"prompt11hxTElLdYiylK18tU5cPtf","nodeInputId":"prompt3hxTElLdYiylK18tU5cPtf","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"从text提取10个关键词用于描述人物,结合这句模板:a photo of boy .front view,motion,canno,blender,3d,render,写一句新的","url":"","temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-en","output":"default","type":"prompt"},"prompt10":{"id":"prompt11hxTElLdYiylK18tU5cPtf","nextId":"prompt12hxTElLdYiylK18tU5cPtf","nodeInputId":"prompt10hxTElLdYiylK18tU5cPtf","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"input","content":"","isQuery":false,"protocol":"https://","query":"#txt2img_prompt textarea","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryInput"},"prompt11":{"id":"prompt12hxTElLdYiylK18tU5cPtf","nextId":"prompt13hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"click","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":"#txt2img_generate","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryClick"},"prompt12":{"id":"prompt13hxTElLdYiylK18tU5cPtf","nextId":"prompt14hxTElLdYiylK18tU5cPtf","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"default","content":"bindCurrentPage","delay":10000,"isQuery":false,"protocol":"https://","query":"","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryDefault"},"prompt13":{"id":"prompt14hxTElLdYiylK18tU5cPtf","nextId":"","nodeInputId":"","role":{"name":"","text":"你是著名设计师Mix,设计了上海世博会,会翻译文献"},"text":"","url":"","queryObj":{"action":"read","content":"bindCurrentPageImages","isQuery":false,"protocol":"https://","query":".thumbnail-item img","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryRead"}}] -------------------------------------------------------------------------------- /examples/combo/知识星球助手_mFnsueSPO86CkHudr6sD2.json: -------------------------------------------------------------------------------- 1 | [{"tag":"知识星球助手","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"combo":6,"interfaces":["contextMenus-page"],"isInfinite":false,"owner":"user","prompt":{"id":"prompt2mFnsueSPO86CkHudr6sD2","nextId":"prompt3mFnsueSPO86CkHudr6sD2","nodeInputId":"","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"","url":"","queryObj":{"action":"read","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":"","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryRead","merged":null},"version":"0.1.0","app":"brainwave","id":"mFnsueSPO86CkHudr6sD2","createDate":1687168073841,"prompt2":{"id":"prompt3mFnsueSPO86CkHudr6sD2","nextId":"prompt4mFnsueSPO86CkHudr6sD2","nodeInputId":"prompt2mFnsueSPO86CkHudr6sD2","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"请全部用中文回答,提取中文摘要,并按照格式,格式里的#要保留(关键词 :##tag_name_1## #tag_name_2#)提取关键词作为标签,用SWOT方法一句话点评本文。最后输出按照格式:\n标题、关键词、摘要、链接、点评","url":"","temperature":0.22,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"translate-zh","output":"markdown","type":"prompt","merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"},{"content":" 请从文本```
\n queryReadRunResult\n

RoleCreate Role78ID: root_8kQ4wffEUYynfQW6FfY_Y ModifyInput176Content ReadingSelectQueryGet Webpage ContentBind Web ContentID: prompt2mFnsueSPO86CkHudr6sD2 ModifyInput2Prompt EngineeringUserInput114Get text from previous node ${context}prompt2mFnsueSPO86CkHudr6sD2ModelChatGPTBingDivergence Degree: 0.3TranslateChineseOutputTextJSON FormatMarkdown FormatTableListID: prompt3mFnsueSPO86CkHudr6sD2 ModifyEnter Web PageURLhttps://DelaymsID: prompt4mFnsueSPO86CkHudr6sD2 ModifySimulate click eventSelectQueryID: prompt5mFnsueSPO86CkHudr6sD2 ModifyText InputSelectQueryText SourceFrom Nodeprompt3mFnsueSPO86CkHudr6sD2ID: prompt6mFnsueSPO86CkHudr6sD2 ModifySimulate click eventSelectQueryID: prompt7mFnsueSPO86CkHudr6sD2 Modify

\n
```提取中文摘要,并按照格式,提取关键词作为标签,一句话点评文本。最后输出按照格式:\n标题、关键词(格式 :#tag_name_1# # tag_name_2#)、摘要、链接、点评, 全部用中文回答,按 Markdown格式输出","role":"user"}]},"prompt3":{"id":"prompt4mFnsueSPO86CkHudr6sD2","nextId":"prompt5mFnsueSPO86CkHudr6sD2","nodeInputId":"","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"","url":"","queryObj":{"action":"default","content":"bindCurrentPage","delay":1000,"isQuery":false,"protocol":"https://","query":"","url":"wx.zsxq.com/dweb2/index/group/48418255514218"},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryDefault","merged":[]},"prompt4":{"id":"prompt5mFnsueSPO86CkHudr6sD2","nextId":"prompt6mFnsueSPO86CkHudr6sD2","nodeInputId":"","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"","url":"","queryObj":{"action":"click","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":".post-topic-head","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryClick","merged":[]},"prompt5":{"id":"prompt6mFnsueSPO86CkHudr6sD2","nextId":"prompt7mFnsueSPO86CkHudr6sD2","nodeInputId":"prompt3mFnsueSPO86CkHudr6sD2","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"","url":"","queryObj":{"action":"input","content":"","isQuery":false,"protocol":"https://","query":".ql-editor","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryInput","merged":[]},"prompt6":{"id":"prompt7mFnsueSPO86CkHudr6sD2","nextId":"","nodeInputId":"","role":{"merged":[{"content":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。","role":"system"}],"name":"","text":"你是知识专家,非常擅长总结文章和挑选适合的分析方法来点评文章。分析方法例如:价值链分析、SWOT分析、费用效益分析、五力模型分析、海星模型、价值创新等方法。"},"text":"","url":"","queryObj":{"action":"click","content":"bindCurrentPage","isQuery":false,"protocol":"https://","query":".submit-btn","url":""},"temperature":0.6,"model":"ChatGPT","input":"nodeInput","userInput":"","translate":"default","output":"default","type":"queryClick","merged":[]}}] -------------------------------------------------------------------------------- /examples/combo/输入网站后跳转_3T2K8wncaKIuerL5rAxpW.json: -------------------------------------------------------------------------------- 1 | [{"tag":"输入网站后跳转","role":{"merged":[],"name":"","text":""},"combo":2,"interfaces":[],"isInfinite":false,"owner":"user","prompt":{"id":"USERINPUTTEXT_TF0RFB9YPR7HCZMCNWM8C","type":"userInputText","inputs":{"nodes":[],"output":""},"userInput":"请输入网站","role":{"merged":[],"name":"","text":""}},"version":"0.3.0","app":"brainwave","id":"3T2K8wncaKIuerL5rAxpW","createDate":1688374214203,"prompt2":{"id":"QUERYDEFAULT_D0WFIAV6LLXCNRRPLURWI","nextId":"","nodeInputId":"USERINPUTTEXT_TF0RFB9YPR7HCZMCNWM8C","role":{"merged":[],"name":"","text":""},"url":"","queryObj":{"action":"default","content":"bindCurrentPage","delay":1000,"protocol":"https://","query":"","url":"${context}"},"input":"nodeInput","userInput":"","type":"queryDefault"}}] -------------------------------------------------------------------------------- /examples/demo01.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/examples/demo01.mp4 -------------------------------------------------------------------------------- /examples/demo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/examples/demo01.png -------------------------------------------------------------------------------- /examples/demo02.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/examples/demo02.mp4 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Earth", 3 | "displayName": "Earth", 4 | "version": "0.3.8.0", 5 | "description": "AGIUI for Browser Extension", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/AGIUI/Earth" 10 | }, 11 | "scripts": { 12 | "start": "ts-node-esm scripts/build.ts --dev", 13 | "build": "ts-node-esm scripts/build.ts" 14 | }, 15 | "type": "module", 16 | "dependencies": { 17 | "@mozilla/readability": "^0.4.2", 18 | "@nem035/gpt-3-encoder": "^1.1.7", 19 | "antd": "^5.3.2", 20 | "eventsource-parser": "^1.0.0", 21 | "i18next": "^22.5.0", 22 | "i18next-browser-languagedetector": "^7.0.2", 23 | "lodash": "^4.17.21", 24 | "markdown-it": "^13.0.1", 25 | "nanoid": "^4.0.2", 26 | "object-hash": "^3.0.0", 27 | "ofetch": "^1.0.1", 28 | "pptxgenjs": "^3.12.0", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-i18next": "^12.3.1", 32 | "reactflow": "^11.7.2", 33 | "styled-components": "^5.3.8", 34 | "ts-md5": "^1.3.1", 35 | "turndown": "^7.1.1", 36 | "uuid": "^9.0.0", 37 | "webextension-polyfill": "^0.10.0", 38 | "websocket-as-promised": "^2.0.1", 39 | "zustand": "^4.3.8" 40 | }, 41 | "devDependencies": { 42 | "@esbuilder/html": "^0.0.6", 43 | "@types/chrome": "^0.0.209", 44 | "@types/fs-extra": "^11.0.1", 45 | "@types/lodash": "^4.14.192", 46 | "@types/markdown-it": "^12.2.3", 47 | "@types/node": "^18.11.18", 48 | "@types/object-hash": "^3.0.2", 49 | "@types/react": "^18.0.27", 50 | "@types/react-dom": "^18.0.10", 51 | "@types/styled-components": "^5.1.26", 52 | "@types/turndown": "^5.0.1", 53 | "@types/uuid": "^9.0.1", 54 | "@types/webextension-polyfill": "^0.10.0", 55 | "@typescript-eslint/eslint-plugin": "^5.48.2", 56 | "@typescript-eslint/parser": "^5.48.2", 57 | "autoprefixer": "^10.4.13", 58 | "concurrently": "^7.6.0", 59 | "esbuild": "^0.17.4", 60 | "esbuild-style-plugin": "^1.6.1", 61 | "eslint": "^8.32.0", 62 | "eslint-config-prettier": "^8.6.0", 63 | "eslint-plugin-import": "^2.27.5", 64 | "eslint-plugin-jsx-a11y": "^6.7.1", 65 | "eslint-plugin-react": "^7.32.1", 66 | "eslint-plugin-react-hooks": "^4.6.0", 67 | "fs-extra": "^11.1.0", 68 | "get-installed-browsers": "^0.1.7", 69 | "rimraf": "^4.1.1", 70 | "ts-node": "^10.9.1", 71 | "typescript": "^4.9.4", 72 | "web-ext": "^7.4.0" 73 | } 74 | } -------------------------------------------------------------------------------- /public/DeepL.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/GPT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/GPT.png -------------------------------------------------------------------------------- /public/Tour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/Tour.png -------------------------------------------------------------------------------- /public/Web.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/all.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/avatars/Designer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/avatars/Designer.png -------------------------------------------------------------------------------- /public/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/background.png -------------------------------------------------------------------------------- /public/chatdoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/chatdoc.png -------------------------------------------------------------------------------- /public/chatgpt-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/chatgpt-icon.png -------------------------------------------------------------------------------- /public/chatgpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/chatgpt.png -------------------------------------------------------------------------------- /public/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/icon-128.png -------------------------------------------------------------------------------- /public/icon-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/icon-34.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/logo.png -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/perplexity-ai.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/perplexity-ai.webp -------------------------------------------------------------------------------- /public/removebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/removebg.png -------------------------------------------------------------------------------- /public/semanticscholar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixLabPro/Earth/edfb2120426f96bfe4dd1477c473261038c9e25e/public/semanticscholar.png -------------------------------------------------------------------------------- /src/assets/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/Template.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | type PropType = { 4 | name: string; 5 | [propName: string]: any; 6 | } 7 | 8 | type StateType = { 9 | name: string; 10 | } 11 | 12 | interface Template { 13 | state: StateType; 14 | props: PropType 15 | } 16 | 17 | class Template extends React.Component { 18 | constructor(props: any) { 19 | super(props); 20 | this.state = { 21 | name: this.props.name || 'template' 22 | } 23 | } 24 | 25 | componentDidMount() { 26 | // this.setupConnection(); 27 | } 28 | 29 | componentDidUpdate(prevProps: { name: string; }, prevState: any) { 30 | if ( 31 | this.props.name !== prevProps.name 32 | ) { 33 | // this.destroyConnection(); 34 | // this.setupConnection(); 35 | } 36 | } 37 | 38 | componentWillUnmount() { 39 | // this.destroyConnection(); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 |
46 | hello world ~ ${this.state.name} 47 |
48 |
49 | ); 50 | } 51 | } 52 | 53 | export default Template; -------------------------------------------------------------------------------- /src/components/background/Agent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * background 3 | */ 4 | 5 | import { chromeStorageSet } from '@components/Utils' 6 | 7 | async function base(combo) { 8 | const markdown = '打开网页' 9 | const data = { 10 | markdown, 11 | combo 12 | } 13 | chrome.storage.local.set({ 'run-agents-result': data }) 14 | return data 15 | } 16 | 17 | // getTextByQuery 18 | async function getTextByQuery(query, combo) { 19 | const doms = document.querySelectorAll(query) 20 | if (doms && doms.length == 0) { 21 | setTimeout(() => getTextByQuery(query, combo), 5000) 22 | return 23 | } 24 | 25 | const markdown = Array.from(doms, t => t.innerText).join('\n') 26 | const data = { 27 | markdown, 28 | query, 29 | combo 30 | } 31 | chrome.storage.local.set({ 'run-agents-result': data }) 32 | return data 33 | } 34 | 35 | 36 | // 知识星球自动发内容 https://wx.zsxq.com/dweb2/index/group/481225281248 37 | async function postTopicForZsxq(text, combo) { 38 | const sleep = (t = 1000) => { 39 | return new Promise((res, rej) => { 40 | setTimeout(() => res(), t) 41 | }) 42 | } 43 | let success = true; 44 | try { 45 | const h = document.querySelector('.post-topic-head'); 46 | h.click(); 47 | await sleep(1000) 48 | const inp = document.querySelector('.ql-editor'); 49 | inp.innerText = text; 50 | await sleep(1000) 51 | const btn = document.querySelector('.submit-btn'); 52 | btn.click() 53 | } catch (error) { 54 | success = false; 55 | } 56 | 57 | const data = { 58 | markdown: success ? '[知识星球发内容] 任务完成' : '任务失败,请重试', 59 | text, 60 | combo 61 | } 62 | chrome.storage.local.set({ 'run-agents-result': data }) 63 | return data 64 | } 65 | 66 | 67 | function run(tabId, data, combo) { 68 | let { query, text, type, delay } = data; 69 | delay = delay || 10000 70 | let code = { target: { tabId }, }; 71 | 72 | if (type == 'queryDefault') { 73 | code = { 74 | ...code, 75 | function: base, 76 | args: [combo] 77 | } 78 | }; 79 | 80 | if (type == 'send-to-zsxq') { 81 | code = { 82 | ...code, 83 | function: postTopicForZsxq, 84 | args: [text, combo] 85 | } 86 | } 87 | 88 | if (code) setTimeout( 89 | () => 90 | chrome.scripting.executeScript(code), 91 | delay 92 | ) 93 | } 94 | 95 | 96 | /** 97 | * 98 | * @param {*} url 99 | * @param {*} data={ query, text,type } 100 | * @param {*} combo 从面板传来的combo数据,用于继续在新的页面运行 101 | */ 102 | const executeScript = (url, data = {}, combo) => { 103 | if (!url) return 104 | chromeStorageSet({ 'run-agents-result': null }) 105 | 106 | url = url + (url.includes('?') ? '&ref=mix' : (url.endsWith('/') ? '?ref=mix' : '/?ref=mix')) 107 | url = url + (url.includes('?') ? '&' : '/?') + 'agents=1&reader=1'; 108 | 109 | chrome.tabs.query({ url, active: true }).then(tabs => { 110 | // 仅保持一个代理网页 111 | for (const tab of tabs) { 112 | chrome.tabs.remove(tab.id) 113 | }; 114 | setTimeout(() => { 115 | chrome.tabs 116 | .create({ url }) 117 | .then(tab => { 118 | run(tab.id, data, combo) 119 | }) 120 | }, 200); 121 | }) 122 | } 123 | 124 | export default { 125 | executeScript 126 | } -------------------------------------------------------------------------------- /src/components/background/Credit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 查询剩余额度 3 | */ 4 | 5 | const apis = { 6 | 'api2d': 'https://openai.api2d.net/dashboard/billing/credit_grants' 7 | } 8 | 9 | 10 | const getPoints = (token, apiName) => { 11 | return fetch(apis[apiName], { 12 | method: 'POST', 13 | headers: { 14 | 'Content-Type': 'application/json', 15 | Authorization: `Bearer ${token}` 16 | } 17 | }).then(r => r.json()).then(res => { 18 | const expireDate = new Date(res.expire_at) 19 | const points = res.total_available; 20 | return { expireDate, points } 21 | }).catch(e => { 22 | return e 23 | }) 24 | } 25 | 26 | export default { 27 | getPoints 28 | } -------------------------------------------------------------------------------- /src/components/buttons/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { CloseOutlined } from '@ant-design/icons'; 3 | 4 | type PropType = { 5 | disabled: boolean; 6 | callback: any; 7 | [propName: string]: any; 8 | } 9 | 10 | type StateType = { 11 | disabled: boolean; 12 | isHovered: boolean; // 新增 isHovered 状态 13 | } 14 | 15 | class CloseButton extends React.Component { 16 | constructor(props: PropType) { 17 | super(props); 18 | this.state = { 19 | disabled: props.disabled, 20 | isHovered: false, // 初始化 isHovered 为 false 21 | } 22 | } 23 | 24 | componentDidUpdate(prevProps: PropType) { 25 | if (this.props.disabled !== prevProps.disabled) { 26 | this.setState({ disabled: this.props.disabled }) 27 | } 28 | } 29 | 30 | handleMouseEnter = () => { 31 | this.setState({ isHovered: true }); 32 | } 33 | 34 | handleMouseLeave = () => { 35 | this.setState({ isHovered: false }); 36 | } 37 | 38 | render() { 39 | const { disabled, isHovered } = this.state; 40 | 41 | return ( 42 | this.props.callback({ cmd: 'close-click' })} 54 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 55 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 56 | /> 57 | ); 58 | } 59 | } 60 | 61 | export default CloseButton; 62 | -------------------------------------------------------------------------------- /src/components/buttons/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | 4 | import { 5 | Dropdown, 6 | Space, 7 | } from 'antd'; 8 | 9 | import type { MenuProps } from 'antd'; 10 | 11 | 12 | import { 13 | DownOutlined 14 | } from '@ant-design/icons'; 15 | 16 | 17 | type PropType = { 18 | callback: any; 19 | data: any; 20 | disabled: boolean; 21 | [propName: string]: any; 22 | } 23 | 24 | type StateType = { 25 | data: any; 26 | open: boolean; 27 | disabled: boolean; 28 | items: any; 29 | } 30 | 31 | interface CopyButton { 32 | state: StateType; 33 | props: PropType 34 | } 35 | 36 | class CopyButton extends React.Component { 37 | constructor(props: any) { 38 | super(props); 39 | 40 | 41 | const items = [ 42 | { 43 | label: '拷贝MarkDown格式', 44 | key: 'markdown', 45 | }, 46 | { 47 | label: '拷贝富文本', 48 | key: 'html', 49 | }, 50 | { 51 | label: '拷贝纯文本', 52 | key: 'text', 53 | }, 54 | ]; 55 | 56 | this.state = { 57 | open: false, 58 | data: this.props.data, 59 | disabled: false, 60 | items: items 61 | } 62 | } 63 | 64 | componentDidMount() { 65 | // this.setupConnection(); 66 | } 67 | 68 | componentDidUpdate(prevProps: { data: any; disabled: boolean }, prevState: any) { 69 | if ( 70 | this.props.data !== prevProps.data 71 | ) { 72 | this.setState({ data: this.props.data }) 73 | // this.destroyConnection(); 74 | // this.setupConnection(); 75 | } 76 | if (this.props.disabled != prevProps.disabled) { 77 | this.setState({ disabled: this.props.disabled }) 78 | } 79 | } 80 | 81 | componentWillUnmount() { 82 | // this.destroyConnection(); 83 | } 84 | 85 | _handleMenuClick: MenuProps['onClick'] = (e) => { 86 | // console.log(e) 87 | // this.props.onClick(e.key) 88 | this._copy(e.key) 89 | this.setState({ 90 | open: false 91 | }) 92 | 93 | }; 94 | 95 | _handleOpenChange = (flag: boolean) => { 96 | this.setState({ 97 | open: flag 98 | }) 99 | }; 100 | 101 | async _copy(e: any) { 102 | 103 | const { text, html, markdown, talks } = this.props.data; 104 | // console.log(text, html, markdown, talks) 105 | let type = "text/html", copyText: any = text; 106 | if (e == 'html') { 107 | type = "text/html"; 108 | copyText = html 109 | } else if (e == 'markdown') { 110 | 111 | } else if (e === 'text') { 112 | type = 'text/plain' 113 | copyText = text; 114 | } 115 | 116 | let isSuccess = false; 117 | 118 | try { 119 | const blob: any = new Blob([copyText], { type }); 120 | const data = [new ClipboardItem({ [type]: blob })]; 121 | // 写入剪切板 122 | await navigator.clipboard.write(data); 123 | 124 | isSuccess = true; 125 | 126 | } catch (e) { 127 | console.error('Failed to copy: ', e); 128 | } 129 | 130 | this.props.callback({ cmd: `copy-${e}`, data: { success: isSuccess } }) 131 | 132 | } 133 | 134 | render() { 135 | return ( 136 | 145 | this._copy('text')}> 146 | 147 | 拷贝 148 | 149 | 150 | 151 | 152 | ); 153 | } 154 | } 155 | 156 | export default CopyButton; -------------------------------------------------------------------------------- /src/components/buttons/DownSquare.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { DownSquareOutlined } from '@ant-design/icons'; 3 | 4 | type PropType = { 5 | disabled: boolean; 6 | callback: any; 7 | onClick: (e: React.MouseEvent) => void; 8 | [propName: string]: any; 9 | } 10 | 11 | type StateType = { 12 | isHovered: boolean; 13 | } 14 | 15 | class DownSquareButton extends React.Component { 16 | state: StateType = { 17 | isHovered: false 18 | }; 19 | 20 | handleMouseEnter = () => { 21 | this.setState({ isHovered: true }); 22 | } 23 | 24 | handleMouseLeave = () => { 25 | this.setState({ isHovered: false }); 26 | } 27 | 28 | render() { 29 | const { onClick, ...restProps } = this.props; 30 | const { isHovered } = this.state; 31 | return ( 32 | 47 | ); 48 | } 49 | } 50 | 51 | export default DownSquareButton; 52 | -------------------------------------------------------------------------------- /src/components/buttons/DownloadButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | DownloadOutlined 4 | } from '@ant-design/icons'; 5 | 6 | type PropType = { 7 | disabled: boolean; 8 | callback: any; 9 | [propName: string]: any; 10 | } 11 | 12 | type StateType = { 13 | disabled: boolean; 14 | isHovered: boolean; // 新增 isHovered 状态 15 | } 16 | 17 | interface DownloadButton { 18 | state: StateType; 19 | props: PropType 20 | } 21 | 22 | class DownloadButton extends React.Component { 23 | constructor(props: PropType) { 24 | super(props); 25 | this.state = { 26 | disabled: props.disabled, 27 | isHovered: false, // 初始化 isHovered 为 false 28 | } 29 | } 30 | 31 | componentDidMount() { 32 | // this.setupConnection(); 33 | } 34 | 35 | componentDidUpdate(prevProps: { disabled: boolean; }, prevState: any) { 36 | if ( 37 | this.props.disabled !== prevProps.disabled 38 | ) { 39 | this.setState({ disabled: this.props.disabled }) 40 | } 41 | } 42 | 43 | handleMouseEnter = () => { 44 | this.setState({ isHovered: true }); 45 | } 46 | 47 | handleMouseLeave = () => { 48 | this.setState({ isHovered: false }); 49 | } 50 | 51 | componentWillUnmount() { 52 | // this.destroyConnection(); 53 | } 54 | 55 | render() { 56 | const { disabled, isHovered } = this.state; 57 | 58 | return ( 59 | this.props.callback({ cmd: 'download-file' })} 71 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 72 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 73 | /> 74 | ); 75 | } 76 | } 77 | 78 | export default DownloadButton; -------------------------------------------------------------------------------- /src/components/buttons/FullscreenButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons'; 3 | 4 | type PropType = { 5 | disabled: boolean; 6 | callback: any; 7 | [propName: string]: any; 8 | } 9 | 10 | type StateType = { 11 | disabled: boolean; 12 | fullscreen: boolean; 13 | isHovered: boolean; // 新增 isHovered 状态 14 | } 15 | 16 | class FullscreenButton extends React.Component { 17 | constructor(props: PropType) { 18 | super(props); 19 | this.state = { 20 | disabled: props.disabled, 21 | fullscreen: props.fullscreen, 22 | isHovered: false, // 初始化 isHovered 为 false 23 | } 24 | } 25 | 26 | componentDidUpdate(prevProps: PropType) { 27 | if (this.props.disabled !== prevProps.disabled) { 28 | this.setState({ disabled: this.props.disabled }) 29 | } 30 | 31 | if (this.props.fullscreen !== prevProps.fullscreen) { 32 | this.setState({ fullscreen: this.props.fullscreen }) 33 | } 34 | } 35 | 36 | handleMouseEnter = () => { 37 | this.setState({ isHovered: true }); 38 | } 39 | 40 | handleMouseLeave = () => { 41 | this.setState({ isHovered: false }); 42 | } 43 | 44 | render() { 45 | const { disabled, fullscreen, isHovered } = this.state; 46 | 47 | return ( 48 | <> 49 | {fullscreen ? ( 50 | this.props.callback({ cmd: 'toggle-fullscreen' })} 61 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 62 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 63 | /> 64 | ) : ( 65 | this.props.callback({ cmd: 'toggle-fullscreen' })} 76 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 77 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 78 | /> 79 | )} 80 | 81 | ); 82 | } 83 | } 84 | 85 | export default FullscreenButton; 86 | -------------------------------------------------------------------------------- /src/components/buttons/HelpButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | QuestionCircleOutlined 4 | } from '@ant-design/icons'; 5 | 6 | type PropType = { 7 | disabled: boolean; 8 | callback: any; 9 | [propName: string]: any; 10 | } 11 | 12 | type StateType = { 13 | disabled: boolean; 14 | isHovered: boolean; // 新增 isHovered 状态 15 | } 16 | 17 | interface OpenFileButton { 18 | state: StateType; 19 | props: PropType 20 | } 21 | 22 | class OpenFileButton extends React.Component { 23 | constructor(props: PropType) { 24 | super(props); 25 | this.state = { 26 | disabled: props.disabled, 27 | isHovered: false, // 初始化 isHovered 为 false 28 | } 29 | } 30 | 31 | handleMouseEnter = () => { 32 | this.setState({ isHovered: true }); 33 | } 34 | 35 | handleMouseLeave = () => { 36 | this.setState({ isHovered: false }); 37 | } 38 | 39 | componentDidMount() { 40 | // this.setupConnection(); 41 | } 42 | 43 | componentDidUpdate(prevProps: { disabled: boolean; }, prevState: any) { 44 | if ( 45 | this.props.disabled !== prevProps.disabled 46 | ) { 47 | this.setState({ disabled: this.props.disabled }) 48 | } 49 | } 50 | 51 | componentWillUnmount() { 52 | // this.destroyConnection(); 53 | } 54 | 55 | render() { 56 | const { disabled, isHovered } = this.state; 57 | 58 | return ( 59 | this.props.callback({ cmd: 'help' })} 71 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 72 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 73 | /> 74 | ); 75 | } 76 | } 77 | 78 | export default OpenFileButton; -------------------------------------------------------------------------------- /src/components/buttons/ImportOfficialCombo.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | PlusOutlined 4 | } from '@ant-design/icons'; 5 | import FullscreenButton from "@components/buttons/FullscreenButton"; 6 | 7 | type PropType = { 8 | disabled: boolean; 9 | callback: any; 10 | [propName: string]: any; 11 | } 12 | 13 | type StateType = { 14 | disabled: boolean; 15 | isHovered: boolean; // 新增 isHovered 状态 16 | } 17 | 18 | interface ImportButton { 19 | state: StateType; 20 | props: PropType 21 | } 22 | 23 | class ImportButton extends React.Component { 24 | constructor(props: PropType) { 25 | super(props); 26 | this.state = { 27 | disabled: props.disabled, 28 | isHovered: false, // 初始化 isHovered 为 false 29 | } 30 | } 31 | 32 | componentDidMount() { 33 | // this.setupConnection(); 34 | } 35 | 36 | componentDidUpdate(prevProps: { disabled: boolean; }, prevState: any) { 37 | if ( 38 | this.props.disabled !== prevProps.disabled 39 | ) { 40 | this.setState({ disabled: this.props.disabled }) 41 | } 42 | } 43 | 44 | componentWillUnmount() { 45 | // this.destroyConnection(); 46 | } 47 | 48 | handleMouseEnter = () => { 49 | this.setState({ isHovered: true }); 50 | } 51 | 52 | handleMouseLeave = () => { 53 | this.setState({ isHovered: false }); 54 | } 55 | 56 | render() { 57 | const { disabled, isHovered } = this.state; 58 | 59 | return ( 60 | this.props.callback({ cmd: 'import-official-combo' })} 72 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 73 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 74 | /> 75 | ) 76 | } 77 | } 78 | 79 | export default ImportButton; -------------------------------------------------------------------------------- /src/components/buttons/OpenFileButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | FolderOpenFilled 4 | } from '@ant-design/icons'; 5 | 6 | type PropType = { 7 | disabled: boolean; 8 | callback: any; 9 | [propName: string]: any; 10 | } 11 | 12 | type StateType = { 13 | disabled: boolean; 14 | isHovered: boolean; // 新增 isHovered 状态 15 | } 16 | 17 | interface OpenFileButton { 18 | state: StateType; 19 | props: PropType 20 | } 21 | 22 | class OpenFileButton extends React.Component { 23 | constructor(props: PropType) { 24 | super(props); 25 | this.state = { 26 | disabled: props.disabled, 27 | isHovered: false, // 初始化 isHovered 为 false 28 | } 29 | } 30 | 31 | handleMouseEnter = () => { 32 | this.setState({ isHovered: true }); 33 | } 34 | 35 | handleMouseLeave = () => { 36 | this.setState({ isHovered: false }); 37 | } 38 | 39 | componentDidMount() { 40 | // this.setupConnection(); 41 | } 42 | 43 | componentDidUpdate(prevProps: { disabled: boolean; }, prevState: any) { 44 | if ( 45 | this.props.disabled !== prevProps.disabled 46 | ) { 47 | this.setState({ disabled: this.props.disabled }) 48 | } 49 | } 50 | 51 | componentWillUnmount() { 52 | // this.destroyConnection(); 53 | } 54 | 55 | render() { 56 | const { disabled, isHovered } = this.state; 57 | 58 | return ( 59 | this.props.callback({ cmd: 'open-file' })} 71 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 72 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 73 | /> 74 | ); 75 | } 76 | } 77 | 78 | export default OpenFileButton; -------------------------------------------------------------------------------- /src/components/buttons/SetupButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { SettingOutlined } from '@ant-design/icons'; 3 | 4 | type PropType = { 5 | disabled: boolean; 6 | callback: any; 7 | [propName: string]: any; 8 | } 9 | 10 | type StateType = { 11 | disabled: boolean; 12 | isHovered: boolean; // 新增 isHovered 状态 13 | } 14 | 15 | class SetupButton extends React.Component { 16 | constructor(props: PropType) { 17 | super(props); 18 | this.state = { 19 | disabled: props.disabled, 20 | isHovered: false, // 初始化 isHovered 为 false 21 | } 22 | } 23 | 24 | componentDidUpdate(prevProps: PropType) { 25 | if (this.props.disabled !== prevProps.disabled) { 26 | this.setState({ disabled: this.props.disabled }) 27 | } 28 | } 29 | 30 | handleMouseEnter = () => { 31 | this.setState({ isHovered: true }); 32 | } 33 | 34 | handleMouseLeave = () => { 35 | this.setState({ isHovered: false }); 36 | } 37 | 38 | render() { 39 | const { disabled, isHovered } = this.state; 40 | 41 | return ( 42 | this.props.callback({ cmd: 'open-setup' })} 54 | onMouseEnter={this.handleMouseEnter} // 绑定 onMouseEnter 事件处理程序 55 | onMouseLeave={this.handleMouseLeave} // 绑定 onMouseLeave 事件处理程序 56 | /> 57 | ); 58 | } 59 | } 60 | 61 | export default SetupButton; 62 | -------------------------------------------------------------------------------- /src/components/chatbot/ChatBotConfig.tsx: -------------------------------------------------------------------------------- 1 | import { workflow } from '@components/flow/Workflow' 2 | import { getConfig, chromeStorageGet } from '@components/Utils'; 3 | import i18n from 'i18next'; 4 | 5 | const json: any = getConfig(); 6 | let discord = json.discord 7 | 8 | 9 | // 从combo构造role选项数据 10 | function createRoleOpts(combo: any, index: number) { 11 | let name = combo.tag || combo.role.name || `R${combo.id.toLocaleUpperCase()}`; 12 | return { 13 | id: combo.id, 14 | type: name, 15 | name: name, 16 | image: combo.role.image || chrome.runtime.getURL(`public/avatars/Designer.png`), 17 | text: combo.role.text, 18 | merged: combo.role.merged, 19 | checked: index == 0 || combo.interfaces && combo.interfaces.includes('role') || false, 20 | role: combo.role, 21 | owner: combo.owner, 22 | _type: "role" 23 | } 24 | } 25 | 26 | function getRoleOpts() { 27 | return new Promise((res, rej) => { 28 | chromeStorageGet(['user', 'offical']).then((data: any) => { 29 | let combo: any = []; 30 | if (data.user) combo = [...data.user]; 31 | if (data.offical) combo = [...combo, ...data.offical]; 32 | // combo=combo.filter((c:any)=>c.interfaces.includes('role')); 33 | combo = Array.from(combo, (c: any, index: number) => { 34 | if (c.role && (c.role.text || c.role.merged) && c.interfaces.includes('role')) { 35 | return createRoleOpts(c, index) 36 | } 37 | }).filter(f => f) 38 | res(combo) 39 | }) 40 | }) 41 | } 42 | 43 | 44 | function get() { 45 | return [{ 46 | id: 'ChatGPT', 47 | type: 'ChatGPT', 48 | name: 'ChatGPT', 49 | icon: chrome.runtime.getURL(`public/chatgpt.png`), 50 | style: { type: 'range', label: 'temperature', value: 0.6, values: [0, 1] }, 51 | checked: true, 52 | _type: "model" 53 | }, { 54 | id: 'Bing', 55 | type: 'Bing', 56 | name: 'New Bing', 57 | icon: chrome.runtime.getURL(`public/bing.svg`), 58 | style: { 59 | type: 'select', 60 | label: i18n.t('styleLabel'), 61 | values: [ 62 | { label: i18n.t('creativeStyleLabel'), value: 'Creative' }, 63 | { label: i18n.t('balancedStyleLabel'), value: 'Balanced' }, 64 | { label: i18n.t('preciseStyleLabel'), value: 'Precise' } 65 | ], 66 | value: 'Creative' 67 | }, 68 | checked: false, 69 | _type: "model" 70 | } 71 | ] 72 | 73 | } 74 | 75 | 76 | 77 | function getInput() { 78 | return Array.from(workflow().inputs, inp => { 79 | if (inp.display.includes('chatbot')) return { 80 | ...inp 81 | } 82 | }).filter(i => i) 83 | } 84 | 85 | function getOutput() { 86 | return Array.from(workflow().outputs, out => { 87 | if (out.display.includes('chatbot')) return { 88 | ...out 89 | } 90 | }).filter(i => i) 91 | } 92 | 93 | function getTranslate() { 94 | return Array.from(workflow().translates, translate => { 95 | if (translate.display.includes('chatbot')) return { 96 | ...translate 97 | } 98 | }).filter(i => i) 99 | } 100 | 101 | function getAgentOpts() { 102 | return Array.from(workflow().agents, (agent: any) => { 103 | if (!agent.disabled && agent.display.includes('chatbot')) return { 104 | value: agent.key, 105 | label: agent.label, 106 | checked: agent.checked 107 | } 108 | }).filter(a => a) 109 | } 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | /** 118 | * 119 | * @param type chatbot-is-available-false 120 | * @param json {hi,buttons,user,html} 121 | * @returns 122 | */ 123 | function createTalkData(type: string, json: any) { 124 | let data; 125 | switch (type) { 126 | case 'chatbot-is-available-false': 127 | data = { 128 | type: 'suggest', 129 | hi: i18n.t('hiCurrentAI', { hi: json.hi }), 130 | buttons: [{ 131 | from: 'open-setup', 132 | data: { 133 | tag: i18n.t('configOrSwitchAI'), 134 | prompt: i18n.t('configOrSwitchAI'), 135 | } 136 | }], 137 | user: false, 138 | html: '' 139 | } 140 | break; 141 | case 'send-talk-refresh': 142 | data = { 143 | type: 'suggest', 144 | hi: i18n.t('historyConversation'), 145 | buttons: [{ 146 | from: 'send-talk-refresh', 147 | data: json.data 148 | }], 149 | user: false, 150 | html: '' 151 | } 152 | break; 153 | case 'new-talk': 154 | data = { 155 | type: 'suggest', 156 | hi: i18n.t('hiWelcome'), 157 | buttons: json.buttons, 158 | user: false, 159 | html: '' 160 | } 161 | break; 162 | case 'ask-user-input': 163 | data = { 164 | type: 'suggest', 165 | hi: `等待ing${''}`, 166 | // buttons: json.buttons, 167 | user: false, 168 | html: json.html 169 | } 170 | break; 171 | case 'more-prompts': 172 | data = { 173 | type: 'suggest', 174 | hi: i18n.t('relatedRecommendations'), 175 | buttons: json.buttons, 176 | user: false, 177 | html: '' 178 | } 179 | break; 180 | case 'urls': 181 | data = { 182 | type: 'suggest', 183 | hi: i18n.t('otherMaterials'), 184 | buttons: json.buttons, 185 | user: false, 186 | html: '' 187 | } 188 | break; 189 | case 'thinking': 190 | data = { 191 | type: 'thinking', 192 | html: '-', 193 | hi: json.hi || i18n.t('thinking') 194 | } 195 | break; 196 | case 'agents': 197 | data = { 198 | type: 'thinking', 199 | html: '-', 200 | hi: i18n.t('fetching') 201 | } 202 | break; 203 | case 'tag': 204 | data = { 205 | type: 'talk', 206 | user: true, 207 | html: json.html 208 | } 209 | break; 210 | case 'debug': 211 | data = { 212 | type: 'talk', 213 | user: false, 214 | html: json.html 215 | } 216 | break; 217 | case 'help': 218 | data = { 219 | type: 'suggest', 220 | hi: i18n.t('howCanIHelp'), 221 | buttons: [{ 222 | from: 'open-url', 223 | data: { 224 | tag: i18n.t('goToCommunity'), 225 | url: discord || 'https://discord.gg/DtAYT2Pt' 226 | } 227 | }], 228 | user: false, 229 | html: '' 230 | } 231 | break; 232 | case 'role-start': 233 | const hi = json.name ? `hi我是${json.name}` : `hi,让我思考下` 234 | data = { 235 | type: 'suggest', 236 | hi, 237 | user: false, 238 | html: json.html, 239 | avatarUrl: json.avatarUrl 240 | } 241 | break; 242 | default: 243 | break; 244 | } 245 | return data 246 | } 247 | 248 | export default { 249 | get, createTalkData, getOutput, getInput, getAgentOpts, getTranslate, getRoleOpts, createRoleOpts 250 | } -------------------------------------------------------------------------------- /src/components/chatbot/ChatBotMiniCard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Card, Button } from 'antd'; 3 | const { Meta } = Card; 4 | 5 | class ChatBotMiniCard extends React.Component<{ 6 | title: string, 7 | yes: any, 8 | no: any 9 | }, { 10 | open: boolean 11 | }> { 12 | constructor(props: any) { 13 | super(props) 14 | this.state = { 15 | open: false 16 | } 17 | } 18 | 19 | componentDidMount(): void { 20 | 21 | setTimeout(() => { 22 | this.setState({ 23 | open: true 24 | }); 25 | setTimeout(() => this.setState({ 26 | open: false 27 | }), 8000) 28 | }, 2000) 29 | } 30 | 31 | render() { 32 | return <> 33 | {this.state.open ? } 47 | 48 | > 49 |
53 | 58 | 60 |
61 | 62 |
74 |
80 | 81 |
: ''} 82 | 83 | } 84 | } 85 | 86 | export default ChatBotMiniCard; 87 | -------------------------------------------------------------------------------- /src/components/chatbot/ChatBotSelect.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Select, Radio, Slider } from 'antd'; 3 | const { Option } = Select 4 | 5 | 6 | /** 7 | * {console.log(cmd,data)}} isLoading={false} 8 | * config={ 9 | * 10 | [ { type,name,style,checked } ] 11 | [{type:'Bing',name:'New Bing',icon:'',style:{type:'select', label:'风格',values:[ 12 | 13 | { label: '创造力', value: 'Creative' }, 14 | { label: '平衡', value: 'Balanced', }, 15 | { label: '严谨', value: 'Precise', } 16 | ],value:'' },checked:false },{type:'ChatGPT',name:'ChatGPT',icon:'',style:{type:'range', label:'temperature',value:0.6 ,values:[0,1]},checked:false }] 17 | * } 18 | * /> 19 | * 20 | */ 21 | 22 | 23 | type PropType = { 24 | name: string; 25 | 26 | /** 回调 27 | * 更改chatbot类型,更改chatbot的风格 28 | * {cmd:'chatbot-select',data:{ type,style: {label,value} }} 29 | */ 30 | callback: any; 31 | 32 | 33 | /**isLoading 正在加载 34 | * 状态:true - 禁用输入 、 false - 可以输入 35 | */ 36 | isLoading: boolean; 37 | 38 | /** 39 | * config 40 | * 传入chatbot类型的配置 [ { type,name,style,checked } ] 41 | * [{type:'Bing',name:'New Bing',icon:'',style:{type:'select', label:'风格',values:[ 42 | * 43 | * { label: '创造力', value: 'Creative' }, 44 | { label: '平衡', value: 'Balanced', }, 45 | { label: '严谨', value: 'Precise', } 46 | * ],value:'' },checked:false },{type:'ChatGPT',name:'ChatGPT',icon:'',style:{type:'range', label:'temperature',value:0.6 ,values:[0,1]},checked:false }] 47 | */ 48 | config: any; 49 | 50 | 51 | [propName: string]: any; 52 | } 53 | 54 | type StateType = { 55 | name: string; 56 | isLoading: boolean; 57 | type: string; 58 | currentName: string; 59 | value: any; 60 | label: string; 61 | icon: string; 62 | config: any; 63 | style: any; 64 | } 65 | 66 | interface ChatBotSelect { 67 | state: StateType; 68 | props: PropType 69 | } 70 | 71 | class ChatBotSelect extends React.Component { 72 | constructor(props: any) { 73 | super(props); 74 | 75 | // 76 | const currentBot =(this.props.config.filter((c: any) => c.checked)[0]) || { 77 | 78 | type: 'ChatGPT', 79 | name: 'ChatGPT', 80 | icon: chrome.runtime.getURL(`public/chatgpt.png`), 81 | style: { type: 'range', label: 'temperature', value: 0.6, values: [0, 1] } 82 | 83 | }; 84 | // console.log('currentBot',currentBot) 85 | 86 | this.state = { 87 | name: this.props.name || 'ChatBotSelect', 88 | isLoading: this.props.isLoading, 89 | type: currentBot?.type, 90 | currentName: currentBot?.name, 91 | style: currentBot?.style, 92 | value: currentBot?.style?.value, 93 | label: currentBot?.style?.label, 94 | icon: currentBot?.icon, 95 | config: this.props.config 96 | } 97 | 98 | } 99 | 100 | _updateStyle(type: string) { 101 | const currentBot = this.state.config.filter((c: any) => c && c.type == type)[0]; 102 | const data = { 103 | type: currentBot?.type, 104 | currentName: currentBot?.name, 105 | style: currentBot?.style, 106 | value: currentBot?.style?.value, 107 | label: currentBot?.style?.label, 108 | icon: currentBot?.icon, 109 | }; 110 | this.setState({ 111 | ...data 112 | }) 113 | return data 114 | } 115 | 116 | componentDidMount() { 117 | console.log('ChatBotSelect componentDidMount') 118 | } 119 | 120 | componentDidUpdate(prevProps: { isLoading: boolean; }, prevState: any) { 121 | if ( 122 | this.props.isLoading !== prevProps.isLoading 123 | ) { 124 | this.setState({ 125 | isLoading: this.props.isLoading 126 | }) 127 | } 128 | } 129 | 130 | componentWillUnmount() { } 131 | 132 | _onChange(type: string) { 133 | 134 | this.setState({ 135 | type 136 | }) 137 | 138 | const { label, value } = this._updateStyle(type); 139 | 140 | this.props.callback({ 141 | cmd: 'chatbot-select', data: { 142 | type: type, 143 | // 根据当前机器人返回设定的值 144 | style: { 145 | label, 146 | value 147 | } 148 | } 149 | }) 150 | } 151 | 152 | _onChangeValue(value: string) { 153 | this.setState({ 154 | value 155 | }) 156 | this.props.callback({ 157 | cmd: 'chatbot-select', data: { 158 | type: this.state.type, 159 | style: { 160 | label: this.state.label, 161 | value 162 | } 163 | } 164 | }) 165 | } 166 | 167 | 168 | render() { 169 | console.log('config::::',this.state.config) 170 | return ( 171 |
176 | 192 | 193 | {/* select : bing 的三个选项 */} 194 | { 195 | this.state.style && this.state.style.type == 'select' ? this._onChangeValue(e.target.value)}> 205 | 206 | { 207 | this.state.style.values.map((v: any) => {v.label}) 211 | } 212 | 213 | : '' 214 | } 215 | 216 | {/* range - chatgpt的选项 temperature: 0.6, */} 217 | { 218 | this.state.style && this.state.style.type == 'range' ? this._onChangeValue(value[0])} 228 | /> : '' 229 | } 230 | 231 |
232 | ) 233 | } 234 | } 235 | 236 | export default ChatBotSelect; -------------------------------------------------------------------------------- /src/components/combo/Agent.tsx: -------------------------------------------------------------------------------- 1 | 2 | const sleep = (t = 1000) => { 3 | return new Promise((res: any, rej) => { 4 | setTimeout(() => res(), t) 5 | }) 6 | } 7 | 8 | // 解析字符串成json,并高亮信息 9 | const highlightText = async (text = '', elements: any) => { 10 | let success = false; 11 | try { 12 | // let itemsIndex = JSON.parse(text); 13 | const target = []; 14 | for (const element of elements) { 15 | if (text.match(element.id)) { 16 | element.style = `background-color: #ffffbb; 17 | color: black; 18 | padding: 8px;` 19 | // console.log(element.id, element) 20 | target.push(element) 21 | } 22 | } 23 | 24 | for (const t of target) { 25 | t.scrollIntoView(); 26 | await sleep(2000) 27 | } 28 | 29 | success = true; 30 | } catch (error) { 31 | 32 | } 33 | return success 34 | } 35 | 36 | // 知识星球自动发内容 https://wx.zsxq.com/dweb2/index/group/481225281248 37 | const postTopicForZsxq = async (text: any) => { 38 | const h: any = document.querySelector('.post-topic-head'); 39 | h.click(); 40 | await sleep(1000) 41 | const inp: any = document.querySelector('.ql-editor'); 42 | inp.innerText = text; 43 | await sleep(1000) 44 | const btn: any = document.querySelector('.submit-btn'); 45 | btn.click() 46 | } 47 | 48 | export { 49 | highlightText,postTopicForZsxq 50 | } -------------------------------------------------------------------------------- /src/components/files/PDF.tsx: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | // import pdfjsLib from '@lib/pdf.min.js' 3 | // //@ts-ignore 4 | // import PdfjsWorker from "@lib/pdf.worker.min.js" 5 | // //@ts-ignore 6 | // import { PDFViewer } from '@lib/pdf_viewer.min.js' 7 | // import '@lib/pdf_viewer.min.css' 8 | // pdfjsLib.GlobalWorkerOptions.workerPort = new PdfjsWorker() 9 | 10 | 11 | const PDF_PATH = "https://arxiv.org/pdf/2305.11175.pdf"; 12 | const PAGE_NUMBER = 1; 13 | const PAGE_SCALE = 1.5; 14 | const SVG_NS = "http://www.w3.org/2000/svg"; 15 | 16 | // async function pageLoaded() { 17 | // console.log('pdf',PDF_PATH) 18 | // // Loading document and page text content 19 | // const loadingTask = pdfjsLib.getDocument({ url:PDF_PATH }); 20 | // const pdfDocument = await loadingTask.promise; 21 | // const page = await pdfDocument.getPage(PAGE_NUMBER); 22 | // const viewport = page.getViewport({ scale: PAGE_SCALE }); 23 | // const textContent = await page.getTextContent(); 24 | // // building SVG and adding that to the DOM 25 | // console.log('pdf',textContent) 26 | // // Release page resources. 27 | // page.cleanup(); 28 | // } 29 | 30 | 31 | 32 | 33 | import * as React from "react"; 34 | 35 | type PropType = { 36 | name: string; 37 | [propName: string]: any; 38 | } 39 | 40 | type StateType = { 41 | name: string; 42 | } 43 | 44 | interface PDF { 45 | state: StateType; 46 | props: PropType 47 | } 48 | 49 | class PDF extends React.Component { 50 | constructor(props: any) { 51 | super(props); 52 | this.state = { 53 | name: this.props.name || 'PDF' 54 | } 55 | // pageLoaded() 56 | } 57 | 58 | componentDidMount() { 59 | // this.setupConnection(); 60 | } 61 | 62 | componentDidUpdate(prevProps: { name: string; }, prevState: any) { 63 | if ( 64 | this.props.name !== prevProps.name 65 | ) { 66 | // this.destroyConnection(); 67 | // this.setupConnection(); 68 | } 69 | } 70 | 71 | componentWillUnmount() { 72 | // this.destroyConnection(); 73 | } 74 | 75 | render() { 76 | return ( 77 |
78 |
79 | hello world ~ ${this.state.name} 80 |
81 |
82 | ); 83 | } 84 | } 85 | 86 | export default PDF; -------------------------------------------------------------------------------- /src/components/files/PPT.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import pptxgen from "pptxgenjs"; 4 | import { getNowDate } from "@src/components/Utils" 5 | 6 | class PPT { 7 | constructor() { } 8 | _createTitle(slide: any, text: string) { 9 | let opts: any = { 10 | x: '10%', 11 | y: '5%', 12 | w: "80%", 13 | h: "20%", 14 | align: "left", 15 | color: "0088CC", 16 | fill: "F1F1F1", 17 | fontSize: 24, 18 | autoFit: true, 19 | fit: "resize", 20 | valign: "top" 21 | } 22 | slide.addText(text, opts); 23 | return slide 24 | } 25 | 26 | _createText(slide: any, text: string) { 27 | slide.addText(text, { 28 | x: '10%', 29 | y: '28%', 30 | w: "80%", 31 | h: "60%", 32 | align: "left", 33 | color: "0088CC", 34 | fill: "F1F1F1", 35 | fontSize: 14, 36 | // autoFit: true, 37 | // fit: "resize", 38 | valign: "top" 39 | }); 40 | return slide 41 | } 42 | 43 | _createImage(slide: pptxgen.Slide, pos: any, base64: any) { 44 | const data: any = { 45 | x: pos.x, 46 | y: pos.y, 47 | w: pos.w, 48 | h: pos.h, 49 | 50 | sizing: { type: "contain", w: pos.w, h: pos.h } 51 | } 52 | if (!base64.match('base64,')) { 53 | data.path = base64; 54 | } else { 55 | data.data = base64; 56 | } 57 | slide.addImage(data); 58 | return slide 59 | } 60 | 61 | _createTable(slide: pptxgen.Slide, rows: any) { 62 | // let rows:any = [ 63 | // ["A1", "B1", "C1"], 64 | // ["aa","bbb","ccc"] 65 | // ]; 66 | 67 | // 格式 68 | rows = Array.from(rows, (row: any, i: number) => { 69 | return Array.from(row, r => { 70 | return { 71 | text: r, options: { 72 | color: "000000", 73 | fill: i === 0 ? "E7E6E6" : "EEEEEE", 74 | bold:i === 0, 75 | fontSize:10 76 | } 77 | } 78 | }) 79 | }) 80 | 81 | slide.addTable(rows, { 82 | align: "left", 83 | fontFace: "Arial", 84 | autoPage: true 85 | }); 86 | return slide 87 | } 88 | 89 | create(fileName: string, items: any = [{ 90 | title: "BONJOUR - CIAO - GUTEN TAG - HELLO - HOLA - NAMASTE - 你好", 91 | images: [{ 92 | title: '示例', 93 | base64: '' 94 | }] 95 | }]) { 96 | let pptx = new pptxgen(); 97 | // pptx.layout = 'LAYOUT_16x9'; 98 | // 10 x 5.625 inches 99 | let layoutW = 10, layoutH = 5.625; 100 | 101 | for (const item of items) { 102 | let slide = pptx.addSlide(); 103 | 104 | if (item.title) { 105 | slide = this._createTitle(slide, item.title) 106 | } 107 | 108 | if (item.text) { 109 | slide = this._createText(slide, item.text) 110 | } 111 | 112 | if (item.images) { 113 | 114 | const getPos = (index: number) => { 115 | let count = item.images.length; 116 | let padding = 10; 117 | let w = layoutW * 0.01 * (100 - padding) / count, 118 | h = w, 119 | x = layoutW * 0.01 * (index + 1) * (padding * 0.5 / count) + index * w, 120 | y = (layoutH - w) / 2; 121 | 122 | return { 123 | x: x, 124 | y: y, 125 | w: w, 126 | h: h 127 | } 128 | } 129 | 130 | for (let index = 0; index < item.images.length; index++) { 131 | let image = item.images[index]; 132 | slide = this._createImage(slide, getPos(index), image.base64) 133 | } 134 | } 135 | 136 | if (item.table) { 137 | slide = this._createTable(slide, item.table) 138 | } 139 | 140 | } 141 | 142 | 143 | return new Promise((res, rej) => { 144 | pptx.writeFile({ fileName: fileName }).then(fileName => { 145 | console.log(`created file: ${fileName}`); 146 | res(fileName) 147 | }); 148 | }) 149 | } 150 | 151 | 152 | createPPT(data: any) { 153 | // console.log('createPPT', data) 154 | 155 | let items: any = []; 156 | 157 | for (const d of data) { 158 | let div = document.createElement('div'); 159 | const { type, html } = d; 160 | div.innerHTML = html; 161 | // div.querySelector('h1'); 162 | 163 | if (div.querySelectorAll('img').length > 0) { 164 | items.push( 165 | { 166 | title: '', 167 | images: Array.from(div.querySelectorAll('img'), im => { 168 | return { 169 | title: '', 170 | base64: im.src 171 | } 172 | }) 173 | } 174 | ); 175 | } else { 176 | console.log('createPPT', div) 177 | if (div.children.length === 1 && div.children[0].tagName == 'TABLE') { 178 | // 表格的处理 179 | const table: any = div.querySelector('table') 180 | const titles = Array.from(table.querySelectorAll('thead th'), (th: any) => th.innerText) 181 | const rows = [titles]; 182 | for (let t of table.querySelectorAll('tbody tr')) { 183 | rows.push(Array.from(t.querySelectorAll('td'), (td: any) => td.innerText)) 184 | }; 185 | 186 | items.push( 187 | { 188 | table: rows 189 | } 190 | ); 191 | 192 | } else { 193 | if (div.innerText) items.push( 194 | { 195 | text: div.innerText, 196 | } 197 | ); 198 | } 199 | 200 | } 201 | 202 | } 203 | 204 | console.log('createPPT', items) 205 | // const p = new PPT(); 206 | return this.create(getNowDate(), items) 207 | 208 | } 209 | } 210 | 211 | export default PPT; -------------------------------------------------------------------------------- /src/components/flow/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { Collapse } from 'antd'; 3 | const { Panel } = Collapse; 4 | import getNodes from '../nodeComponents/index' 5 | import i18n from "i18next"; 6 | 7 | export default (props: any) => { 8 | const onDragStart = (event: any, nodeType: string, dataType: string) => { 9 | event.dataTransfer.setData('application/reactflow', nodeType); 10 | event.dataTransfer.setData('application/dataType', dataType); 11 | event.dataTransfer.effectAllowed = 'move'; 12 | }; 13 | const onChange: any = (key: string) => { 14 | console.log(key); 15 | }; 16 | 17 | const { newNodes } = props; 18 | 19 | const [nodes, setNodes] = React.useState(getNodes()); 20 | 21 | useEffect(() => { 22 | console.log('sidebar newNodes:', newNodes) 23 | if (newNodes && newNodes.length > 0) setNodes(newNodes) 24 | }, [newNodes]) 25 | 26 | // console.log(Array.from(nodes.filter((n: any) => n.open), n => n.title)) 27 | return ( 28 | n.open), n => n.title)[0]} 30 | onChange={onChange} 31 | style={{ 32 | width: 180, userSelect: 'none', 33 | background: 'white', 34 | }}> 35 |

{i18n.t('component')}

36 | { 37 | Array.from(nodes, (node: any) => { 38 | return 39 | 65 | 66 | }) 67 | } 68 |
69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /src/components/flow/edges/BWEdge.tsx: -------------------------------------------------------------------------------- 1 | import { BaseEdge, EdgeProps, getStraightPath } from 'reactflow'; 2 | 3 | function BWEdge(props: EdgeProps) { 4 | const { sourceX, sourceY, targetX, targetY } = props; 5 | 6 | const [edgePath] = getStraightPath({ 7 | sourceX, 8 | sourceY, 9 | targetX, 10 | targetY, 11 | }); 12 | 13 | return ; 14 | } 15 | 16 | export default BWEdge; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/CustomPromptNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | 5 | import i18n from "i18next"; 6 | 7 | import {createCardTitle, createDebug, SelectNodeInput, createText, createSelect, createOutput, createModel, nodeStyle, getI18n } from './Base'; 8 | // import { i18nInit } from '../i18nConfig'; 9 | 10 | 11 | 12 | function Main({ id, data, selected }: any) { 13 | // i18nInit(); 14 | const { debugMenu, contextMenus } = getI18n(); 15 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 16 | let merged: any = data.merged; 17 | if (merged && merged.length == 0) { 18 | merged = [ 19 | { 20 | role: 'system', 21 | content: '' 22 | }, 23 | { 24 | role: 'user', 25 | content: '' 26 | } 27 | ] 28 | } 29 | const [debugInput, setDebugInput] = React.useState((merged ? JSON.stringify(merged, null, 2) : "")); 30 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 31 | 32 | // 模型 33 | const models = data.opts.models; 34 | const [model, setModel] = React.useState(data.model) 35 | const [temperature, setTemperature] = React.useState(data.temperature); 36 | 37 | // input 38 | const [input, setInput] = React.useState(data.input) 39 | const [nodeInputId, setNodeInputId] = React.useState(data.nodeInputId) 40 | 41 | 42 | // output 43 | const outputs = data.opts.outputs; 44 | 45 | const [output, setOutput] = React.useState(data.output) 46 | 47 | const updateData = (e: any) => { 48 | // console.log(e) 49 | if (e.key === 'model') { 50 | setModel(e.data); 51 | data.onChange({ id, data: { model: e.data } }) 52 | } 53 | if (e.key === 'temperature') { 54 | setTemperature(e.data); 55 | data.onChange({ id, data: { temperature: e.data } }) 56 | } 57 | 58 | if (e.key === 'debugInput') { 59 | setDebugInput(e.data); 60 | let json: any=[]; 61 | try { 62 | json = JSON.parse(e.data); 63 | setStatusInputForDebug('') 64 | } catch (error) { 65 | setStatusInputForDebug('error') 66 | } 67 | 68 | data.onChange({ id, data: { debugInput: e.data, merged: json } }); 69 | 70 | } 71 | 72 | if (e.key === 'input') { 73 | setInput(e.data); 74 | data.onChange({ 75 | id, data: { 76 | input: e.data 77 | } 78 | }) 79 | } 80 | 81 | if (e.key === 'output') { 82 | setOutput(e.data); 83 | data.onChange({ id, data: { output: e.data } }) 84 | } 85 | 86 | if (e.key == "nodeInput") { 87 | setNodeInputId(e.data); 88 | data.onChange({ 89 | id, 90 | data: { 91 | nodeInputId: e.data, 92 | input: e.data ? 'nodeInput' : 'default' 93 | } 94 | }) 95 | } 96 | 97 | 98 | if (e.key == "debug") data.onChange({ id, data: e.data }) 99 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 100 | } 101 | 102 | 103 | 104 | 105 | const createNode = () => { 106 | const node = []; 107 | 108 | let nodeOpts: any[] = []; 109 | if (data.getNodes) nodeOpts = [...data.getNodes(id)] 110 | let selectNodeValue = input === "nodeInput" ? (nodeInputId) : null 111 | // console.log('selectNodeValue',selectNodeValue,nodeInputId,nodeOpts[0],data) 112 | // setNodeInputId(selectNodeValue) 113 | 114 | // if (data.debugInput != debugInput && shouldRefresh) { 115 | // setDebugInput(data.debugInput); 116 | // setShouldRefresh(false) 117 | // } 118 | 119 | node.push( 120 | createText('debugInput', i18n.t('custom'), '', debugInput, statusInputForDebug, updateData) 121 | ) 122 | node.push( 123 | 129 | ) 130 | 131 | 132 | 133 | 134 | node.push(createModel(model, temperature, models, updateData)) 135 | 136 | node.push( 137 | createDebug(debugMenu, id, 138 | debugInput, 139 | data.debugOutput, 140 | (event: any) => { 141 | if (event.key == 'input') { 142 | const { data } = event; 143 | setDebugInput(data) 144 | let json: any; 145 | try { 146 | json = JSON.parse(data); 147 | setStatusInputForDebug('') 148 | updateData({ 149 | key: 'debugInput', 150 | data: data 151 | }) 152 | } catch (error) { 153 | setStatusInputForDebug('error') 154 | } 155 | }; 156 | if (event.key == 'draggable') updateData(event) 157 | }, 158 | () => { 159 | console.log('debugFun debugInput', debugInput) 160 | if (debugInput != "" && debugInput && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 161 | let merged; 162 | try { 163 | merged = JSON.parse(debugInput) 164 | } catch (error) { 165 | 166 | } 167 | console.log('debugFun merged', merged) 168 | data.merged = merged; 169 | data.debugInput = JSON.stringify(merged, null, 2); 170 | if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 171 | data.debug && data.debug(data); 172 | } else if (debugInput == "" || debugInput && debugInput.replace(/\s/ig, "") == "[]") { 173 | data.merged = null; 174 | data.debugInput = ""; 175 | if (data.role) data.role.merged = null; 176 | console.log('debugFun no merged', data) 177 | data.debug && data.debug(data) 178 | setShouldRefresh(true); 179 | } else if (debugInput === undefined) { 180 | data.debug && data.debug(data); 181 | setShouldRefresh(true); 182 | } 183 | }, 184 | () => data.merge && data.merge(data), 185 | { 186 | statusInput: statusInputForDebug, 187 | statusOutput: "" 188 | }) 189 | ) 190 | 191 | return 197 | {...node} 198 | 199 | } 200 | 201 | return ( 202 | 203 |
208 | {createNode()} 209 | 210 | 211 |
212 |
213 | ); 214 | } 215 | 216 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/FilePPTCreateNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | 4 | import { Card, Dropdown, Button, } from 'antd'; 5 | 6 | import { createCardTitle, createDebug, nodeStyle, selectNodeInputBase, getI18n } from './Base' 7 | 8 | import i18n from "i18next"; 9 | 10 | 11 | function Main({ id, data, selected }: any) { 12 | 13 | // i18nInit(); 14 | const { debugMenu, contextMenus } = getI18n(); 15 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 16 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 17 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 18 | 19 | 20 | const updateData = (e: any) => { 21 | if (e.key == "debug") data.onChange({ id, data: e.data }) 22 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }); 23 | if (e.key == 'inputs') data.onChange({ 24 | id, data: { 25 | file: { 26 | inputs: e.data, 27 | type: 'ppt' 28 | } 29 | } 30 | }); 31 | if (e.key === "nodeInput") { 32 | // console.log(inputs, e.index, e.data,nodeOpts.filter((n: any) => n.value == e.data)); 33 | const inps = Array.from(inputs, (i: string, index) => { 34 | if (index === e.index) i = e.data; 35 | return i; 36 | }); 37 | // console.log(JSON.stringify(inps,null,2)) 38 | setInputs(inps) 39 | data.onChange({ 40 | id, 41 | data: { 42 | file: { 43 | inputs: inps, 44 | type: 'ppt' 45 | } 46 | } 47 | }); 48 | } 49 | 50 | 51 | if (e.key === "nodeInput-onClick") { 52 | // const otherNodes = data.getNodes(e.data); 53 | // console.log('nodeInput-onClick',otherNodes) 54 | // data.onChange({ 55 | // id: e.data, 56 | // data: { 57 | // hightlight: true 58 | // } 59 | // }) 60 | } 61 | } 62 | 63 | let nodeOpts: any[] = []; 64 | if (data.getNodes) nodeOpts = [...data.getNodes(id)]; 65 | 66 | const file: any = data.file; 67 | const [inputs, setInputs] = React.useState(file?.inputs || [nodeOpts[0].value]) 68 | 69 | // console.log(inputs) 70 | 71 | const createNode = (role: any) => { 72 | 73 | if (shouldRefresh && data.debugInput != debugInput) { 74 | setDebugInput(data.debugInput); 75 | } 76 | 77 | return 85 |
{ 90 | updateData({ 91 | key: 'draggable', 92 | data: false 93 | }) 94 | }} 95 | onMouseLeave={() => { 96 | updateData({ 97 | key: 'draggable', 98 | data: true 99 | }) 100 | }} 101 | > 102 | 111 | 112 | 121 | 122 | { 123 | inputs.length > 0 ? 124 | Array.from( 125 | inputs, 126 | (inp: any, i) => 127 |
{ 128 | selectNodeInputBase(inp, nodeOpts, (e: any) => updateData({ ...e, index: i })) 129 | }
130 | ) : '' 131 | } 132 |
133 | {/* { 134 | createDebug(debugMenu, id, 135 | debugInput, 136 | data.debugOutput, 137 | (event: any) => { 138 | if (event.key == 'input') { 139 | const { data } = event; 140 | setDebugInput(data) 141 | let json: any; 142 | try { 143 | json = JSON.parse(data); 144 | setStatusInputForDebug('') 145 | } catch (error) { 146 | setStatusInputForDebug('error') 147 | } 148 | }; 149 | if (event.key == 'draggable') updateData(event) 150 | }, 151 | () => { 152 | console.log('debugFun debugInput', debugInput) 153 | if (debugInput != "" && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 154 | let merged; 155 | try { 156 | merged = JSON.parse(debugInput) 157 | } catch (error) { 158 | 159 | } 160 | console.log('debugFun merged', merged) 161 | data.merged = merged; 162 | data.debugInput = JSON.stringify(merged, null, 2); 163 | if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 164 | data.debug && data.debug(data); 165 | } else if (debugInput == "" || debugInput.replace(/\s/ig, "") == "[]") { 166 | data.merged = null; 167 | data.debugInput = ""; 168 | if (data.role) data.role.merged = null; 169 | console.log('debugFun no merged', data) 170 | data.debug && data.debug(data) 171 | setShouldRefresh(true); 172 | } else if (debugInput === undefined) { 173 | data.debug && data.debug(data) 174 | } 175 | }, 176 | () => data.merge && data.merge(data), 177 | { 178 | statusInput: statusInputForDebug, 179 | statusOutput: "" 180 | }) 181 | } */} 182 |
183 | } 184 | 185 | return ( 186 | 189 |
195 | {createNode("4")} 196 | 197 | 198 | 199 |
200 |
201 | ); 202 | } 203 | 204 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/InputMergeNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow'; 3 | 4 | import { Card, Select, Radio, InputNumber, Dropdown, Space, Button, Divider, MenuProps } from 'antd'; 5 | 6 | import {createCardTitle, createText, nodeStyle, selectNodeInputBase, getI18n } from './Base' 7 | 8 | import i18n from "i18next"; 9 | 10 | 11 | function Main({ id, data, selected }: any) { 12 | 13 | // i18nInit(); 14 | const { debugMenu, contextMenus } = getI18n(); 15 | 16 | 17 | let nodeOpts: any[] = []; 18 | if (data.getNodes) nodeOpts = [...data.getNodes(id)]; 19 | 20 | const [inputs, setInputs] = React.useState(data.inputs?.nodes || []) 21 | const [output, setOutput] = React.useState(data.inputs?.output || '') 22 | 23 | const updateData = (e: any) => { 24 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }); 25 | if (e.key == 'inputs') data.onChange({ 26 | id, data: { 27 | inputs: { 28 | ...data.inputs, 29 | nodes: e.data, 30 | } 31 | } 32 | }); 33 | if (e.key === "nodeInput") { 34 | // console.log(inputs, e.index, e.data,nodeOpts.filter((n: any) => n.value == e.data)); 35 | const inps = Array.from(inputs, (i: string, index) => { 36 | if (index === e.index) i = e.data; 37 | return i; 38 | }); 39 | // console.log(JSON.stringify(inps,null,2)) 40 | setInputs(inps) 41 | data.onChange({ 42 | id, 43 | data: { 44 | inputs: { 45 | ...data.inputs, 46 | nodes: inps 47 | } 48 | } 49 | }); 50 | } 51 | 52 | if (e.key == 'output') { 53 | setOutput(e.data) 54 | data.onChange({ 55 | id, data: { 56 | inputs: { 57 | ...data.inputs, 58 | output: e.data, 59 | } 60 | } 61 | }); 62 | } 63 | 64 | } 65 | 66 | 67 | 68 | // console.log(inputs) 69 | 70 | const createNode = (role: any) => { 71 | return 77 |
{ 82 | updateData({ 83 | key: 'draggable', 84 | data: false 85 | }) 86 | }} 87 | onMouseLeave={() => { 88 | updateData({ 89 | key: 'draggable', 90 | data: true 91 | }) 92 | }} 93 | > 94 | 103 | 104 | 113 | 114 | { 115 | inputs.length > 0 ? 116 | Array.from( 117 | inputs, 118 | (inp: any, i) => 119 |
{ 120 | selectNodeInputBase(inp, nodeOpts, (e: any) => updateData({ ...e, index: i })) 121 | }
122 | ) : '' 123 | } 124 | 125 | 126 | {createText('output', i18n.t('mergeReg'), '', output, '', updateData)} 127 |

{i18n.t('inputMergePlaceholderTips')}

128 | 129 |
130 | 131 | 132 | 133 |
134 | } 135 | 136 | return ( 137 | 140 |
146 | {createNode("4")} 147 | 148 | 149 | 150 | 151 |
152 |
153 | ); 154 | } 155 | 156 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/PromptNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | 5 | import i18n from "i18next"; 6 | 7 | import { 8 | createCardTitle, createDebug, SelectNodeInput, 9 | createText, createSelect, createOutput, createModel, nodeStyle, getI18n 10 | } from './Base'; 11 | 12 | 13 | function Main({ id, data, selected }: any) { 14 | // i18nInit(); 15 | const { debugMenu, contextMenus } = getI18n(); 16 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 17 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 18 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 19 | 20 | // 模型 21 | const models = data.opts.models; 22 | const [model, setModel] = React.useState(data.model) 23 | const [temperature, setTemperature] = React.useState(data.temperature); 24 | 25 | // input 26 | const [input, setInput] = React.useState(data.input) 27 | const [nodeInputId, setNodeInputId] = React.useState(data.nodeInputId) 28 | // text 29 | const [text, setText] = React.useState(data.text) 30 | 31 | // output 32 | const translates = data.opts.translates; 33 | const [translate, setTranslate] = React.useState(data.translate) 34 | 35 | // output 36 | const outputs = data.opts.outputs; 37 | 38 | const [output, setOutput] = React.useState(data.output) 39 | 40 | const updateData = (e: any) => { 41 | // console.log(e) 42 | if (e.key === 'model') { 43 | setModel(e.data); 44 | data.onChange({ id, data: { model: e.data } }) 45 | } 46 | if (e.key === 'temperature') { 47 | setTemperature(e.data); 48 | data.onChange({ id, data: { temperature: e.data } }) 49 | } 50 | 51 | if (e.key === 'text') { 52 | setText(e.data); 53 | data.onChange({ id, data: { text: e.data, debugInput: "" } }); 54 | setShouldRefresh(true); 55 | // console.log('updateData:',e,shouldRefresh) 56 | } 57 | 58 | if (e.key == 'debugInput') { 59 | data.onChange({ 60 | id, data: { 61 | debugInput: e.data 62 | } 63 | }) 64 | } 65 | 66 | // nodeinput 67 | if (e.key === 'input') { 68 | setInput(e.data); 69 | data.onChange({ 70 | id, data: { 71 | input: e.data 72 | } 73 | }) 74 | } 75 | 76 | if (e.key === 'output') { 77 | setOutput(e.data); 78 | data.onChange({ id, data: { output: e.data } }) 79 | } 80 | 81 | if (e.key == "nodeInput") { 82 | setNodeInputId(e.data); 83 | data.onChange({ 84 | id, 85 | data: { 86 | nodeInputId: e.data, 87 | input: e.data ? 'nodeInput' : 'default' 88 | } 89 | }) 90 | } 91 | 92 | // console.log(e) 93 | if (e.key === i18n.t('translate')) { 94 | setTranslate(e.data); 95 | data.onChange({ id, data: { translate: e.data } }) 96 | } 97 | 98 | 99 | if (e.key == "debug") data.onChange({ id, data: e.data }) 100 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 101 | } 102 | 103 | if (data.debugInput != debugInput && shouldRefresh) { 104 | setDebugInput(data.debugInput); 105 | setShouldRefresh(false) 106 | } 107 | 108 | 109 | const createNode = () => { 110 | const node = []; 111 | 112 | let nodeOpts: any[] = []; 113 | if (data.getNodes) nodeOpts = [...data.getNodes(id)] 114 | let selectNodeValue = input === "nodeInput" ? (nodeInputId) : null 115 | // console.log('nodeOpts', nodeOpts, data) 116 | // setNodeInputId(selectNodeValue) 117 | 118 | node.push( 119 | createText('text', i18n.t('userInput'), '', text, '', updateData) 120 | ) 121 | 122 | node.push( 128 | ) 129 | 130 | node.push(createModel(model, temperature, models, updateData)) 131 | 132 | node.push( 133 | createSelect(i18n.t('translate'), translate, translates, updateData) 134 | ) 135 | 136 | node.push(createOutput(i18n.t('outputText'), 'output', output, outputs, updateData)) 137 | 138 | node.push( 139 | createDebug(debugMenu, id, 140 | debugInput, 141 | data.debugOutput, 142 | (event: any) => { 143 | if (event.key == 'input') { 144 | const { data } = event; 145 | setDebugInput(data) 146 | let json: any; 147 | try { 148 | json = JSON.parse(data); 149 | setStatusInputForDebug('') 150 | updateData({ 151 | key: 'debugInput', 152 | data: data 153 | }) 154 | } catch (error) { 155 | setStatusInputForDebug('error') 156 | } 157 | }; 158 | if (event.key == 'draggable') updateData(event) 159 | }, 160 | () => { 161 | console.log('debugFun debugInput', debugInput) 162 | if (debugInput != "" && debugInput && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 163 | let merged; 164 | try { 165 | merged = JSON.parse(debugInput) 166 | } catch (error) { 167 | 168 | } 169 | console.log('debugFun merged', merged) 170 | data.merged = merged; 171 | data.debugInput = JSON.stringify(merged, null, 2); 172 | if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 173 | data.debug && data.debug(data); 174 | } else if (debugInput == "" || debugInput && debugInput.replace(/\s/ig, "") == "[]") { 175 | data.merged = null; 176 | data.debugInput = ""; 177 | if (data.role) data.role.merged = null; 178 | console.log('debugFun no merged', data) 179 | data.debug && data.debug(data) 180 | setShouldRefresh(true); 181 | } else if (debugInput === undefined) { 182 | data.debug && data.debug(data); 183 | setShouldRefresh(true); 184 | } 185 | }, 186 | () => data.merge && data.merge(data), 187 | { 188 | statusInput: statusInputForDebug, 189 | statusOutput: "" 190 | }) 191 | ) 192 | 193 | 194 | return 200 | {...node} 201 | 202 | } 203 | 204 | return ( 205 | 206 |
211 | {createNode()} 212 | 213 | 214 |
215 |
216 | ); 217 | } 218 | 219 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/QueryClickNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | 5 | import { createCardTitle,createDebug, createTextArea, nodeStyle, getI18n } from './Base' 6 | 7 | import i18n from "i18next"; 8 | // import { i18nInit } from '../i18nConfig'; 9 | 10 | 11 | const createUrl = (title: string, json: any, onChange: any) => { 12 | const { protocol, url, init, query } = json; 13 | const key = 'query'; 14 | return
{ 15 | onChange({ 16 | key: 'draggable', 17 | data: false 18 | }) 19 | }} 20 | onMouseLeave={() => { 21 | onChange({ 22 | key: 'draggable', 23 | data: true 24 | }) 25 | }}> 26 | 27 | { 28 | createTextArea(title, query, i18n.t('queryClickPlaceholder'), "", (e: any) => { 29 | const data = { 30 | ...json, 31 | query: e.data, 32 | action: 'click' 33 | } 34 | 35 | onChange({ 36 | key, 37 | data 38 | }) 39 | }) 40 | } 41 | 42 |
43 | } 44 | 45 | 46 | function Main({ id, data, selected }: any) { 47 | // i18nInit(); 48 | const { debugMenu, contextMenus } = getI18n(); 49 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 50 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 51 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 52 | 53 | 54 | // queryObj 55 | const [queryObj, setQueryObj] = React.useState(data.queryObj) 56 | const updateData = (e: any) => { 57 | // console.log(e) 58 | if (e.key === 'query') { 59 | setQueryObj(e.data); 60 | data.onChange({ id, data: { queryObj: e.data } }) 61 | } 62 | if (e.key == "debug") data.onChange({ id, data: e.data }) 63 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 64 | } 65 | 66 | const createNode = () => { 67 | const node = [createUrl(i18n.t('selectQuery'), queryObj, updateData)]; 68 | 69 | if (data.debugInput != debugInput && shouldRefresh) { 70 | setDebugInput(data.debugInput); 71 | setShouldRefresh(false) 72 | } 73 | 74 | // node.push( 75 | // createDebug(debugMenu, id, 76 | // debugInput, 77 | // data.debugOutput, 78 | // (event: any) => { 79 | // if (event.key == 'input') { 80 | // const { data } = event; 81 | // setDebugInput(data) 82 | // let json: any; 83 | // try { 84 | // json = JSON.parse(data); 85 | // setStatusInputForDebug('') 86 | // } catch (error) { 87 | // setStatusInputForDebug('error') 88 | // } 89 | // }; 90 | // if (event.key == 'draggable') updateData(event) 91 | // }, 92 | // () => { 93 | // console.log('debugFun debugInput', debugInput) 94 | // if (debugInput != "" && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 95 | // let merged; 96 | // try { 97 | // merged = JSON.parse(debugInput) 98 | // } catch (error) { 99 | 100 | // } 101 | // console.log('debugFun merged', merged) 102 | // data.merged = merged; 103 | // data.debugInput = JSON.stringify(merged, null, 2); 104 | // if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 105 | // data.debug && data.debug(data); 106 | // } else if (debugInput == "" || debugInput.replace(/\s/ig, "") == "[]") { 107 | // data.merged = null; 108 | // data.debugInput = ""; 109 | // if (data.role) data.role.merged = null; 110 | // console.log('debugFun no merged', data) 111 | // data.debug && data.debug(data) 112 | // setShouldRefresh(true); 113 | // }else if (debugInput === undefined) { 114 | // data.debug && data.debug(data) 115 | // } 116 | // }, 117 | // () => data.merge && data.merge(data), 118 | // { 119 | // statusInput: statusInputForDebug, 120 | // statusOutput: "" 121 | // }) 122 | // ) 123 | 124 | return 129 | {...node} 130 | 131 | } 132 | 133 | return ( 134 | 135 |
140 | {createNode()} 141 | 142 | 143 |
144 |
145 | ); 146 | } 147 | 148 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/QueryDefaultNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | 5 | import i18n from "i18next"; 6 | 7 | import { createCardTitle, SelectNodeInput, createURL, createDelay, getI18n, nodeStyle } from './Base' 8 | // import { i18nInit } from '../i18nConfig'; 9 | 10 | 11 | const createUI = (json: any, delay: number, delayFormat: string, selectNodeValue: string, nodeOpts: any, onChange: any) => { 12 | const { protocol, url } = json; 13 | 14 | return
{ 15 | onChange({ 16 | key: 'draggable', 17 | data: false 18 | }) 19 | }} 20 | onMouseLeave={() => { 21 | onChange({ 22 | key: 'draggable', 23 | data: true 24 | }) 25 | }}> 26 | 27 | { 28 | createURL(i18n.t('url'), i18n.t('urlPlaceholder'), protocol, url, (e: any) => { 29 | let d = { 30 | ...json, 31 | protocol: e.data 32 | } 33 | if (e.key == 'url') { 34 | d = { 35 | ...json, 36 | url: e.data, 37 | action: 'default' 38 | } 39 | } 40 | onChange({ 41 | key: 'query', 42 | data: d 43 | }) 44 | }) 45 | } 46 | 47 | 53 | 54 | { 55 | createDelay(i18n.t('delay'), delayFormat, delay.toString(), [ 56 | { value: 'ms', label: i18n.t('ms') }, 57 | { value: 's', label: i18n.t('s') }], (e: any) => { 58 | if (e.key == 'delay') { 59 | onChange({ 60 | key: 'delay', 61 | data: e.data 62 | }) 63 | } 64 | }) 65 | } 66 | 67 | 68 |
69 | } 70 | 71 | 72 | function Main({ id, data, selected }: any) { 73 | // i18nInit(); 74 | const { debugMenu, contextMenus } = getI18n(); 75 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 76 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 77 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 78 | 79 | // queryObj 80 | // data.queryObj.isQuery = type === "query"; 81 | const [queryObj, setQueryObj] = React.useState(data.queryObj) 82 | const [delayFormat, setDelayFormat] = React.useState('ms'); 83 | 84 | const [delay, setDelay] = React.useState(queryObj.delay || 1000) 85 | 86 | 87 | const [input, setInput] = React.useState(data.input) 88 | const [nodeInputId, setNodeInputId] = React.useState(data.nodeInputId) 89 | 90 | 91 | const updateData = (e: any) => { 92 | // console.log(e) 93 | if (e.key === 'query') { 94 | setQueryObj(e.data); 95 | data.onChange({ id, data: { queryObj: e.data } }) 96 | } 97 | 98 | if (e.key == "nodeInput") { 99 | setNodeInputId(e.data); 100 | data.onChange({ 101 | id, 102 | data: { 103 | nodeInputId: e.data, 104 | input: e.data ? 'nodeInput' : 'default' 105 | } 106 | }) 107 | } 108 | 109 | 110 | if (e.key == "debug") data.onChange({ id, data: e.data }) 111 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 112 | 113 | if (e.key === "delay") { 114 | const { delay, delayFormat } = e.data; 115 | let d = delay; 116 | if (delayFormat == 's') d = d * 1000; 117 | // console.log(d,delayFormat) 118 | setQueryObj({ 119 | ...queryObj, 120 | delay: d 121 | }); 122 | setDelay(delay) 123 | setDelayFormat(delayFormat) 124 | data.onChange({ 125 | id, data: { 126 | queryObj: { 127 | ...queryObj, 128 | delay: d 129 | } 130 | } 131 | }) 132 | } 133 | 134 | } 135 | 136 | 137 | const createNode = () => { 138 | 139 | if (data.debugInput != debugInput && shouldRefresh) { 140 | setDebugInput(data.debugInput); 141 | setShouldRefresh(false) 142 | } 143 | 144 | 145 | let nodeOpts: any[] = []; 146 | if (data.getNodes) nodeOpts = [...data.getNodes(id)] 147 | let selectNodeValue = input === "nodeInput" ? (nodeInputId) : null 148 | 149 | // console.log(delay, delayFormat) 150 | const node = [ 151 | createUI(queryObj, delay, delayFormat, selectNodeValue, nodeOpts, updateData) 152 | ]; 153 | 154 | // node.push( 155 | // createDebug(debugMenu, id, 156 | // debugInput, 157 | // data.debugOutput, 158 | // (event: any) => { 159 | // if (event.key == 'input') { 160 | // const { data } = event; 161 | // setDebugInput(data) 162 | // let json: any; 163 | // try { 164 | // json = JSON.parse(data); 165 | // setStatusInputForDebug('') 166 | // } catch (error) { 167 | // setStatusInputForDebug('error') 168 | // } 169 | // }; 170 | // if (event.key == 'draggable') updateData(event) 171 | // }, 172 | // () => { 173 | // console.log('debugFun debugInput', debugInput) 174 | // if (debugInput != "" && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 175 | // let merged; 176 | // try { 177 | // merged = JSON.parse(debugInput) 178 | // } catch (error) { 179 | 180 | // } 181 | // console.log('debugFun merged', merged) 182 | // data.merged = merged; 183 | // data.debugInput = JSON.stringify(merged, null, 2); 184 | // if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 185 | // data.debug && data.debug(data); 186 | // } else if (debugInput == "" || debugInput.replace(/\s/ig, "") == "[]") { 187 | // data.merged = null; 188 | // data.debugInput = ""; 189 | // if (data.role) data.role.merged = null; 190 | // console.log('debugFun no merged', data) 191 | // data.debug && data.debug(data) 192 | // setShouldRefresh(true); 193 | // } else if (debugInput === undefined) { 194 | // data.debug && data.debug(data) 195 | // } 196 | // }, 197 | // () => data.merge && data.merge(data), 198 | // { 199 | // statusInput: statusInputForDebug, 200 | // statusOutput: "" 201 | // }) 202 | // ) 203 | 204 | return 211 | {...node} 212 | 213 | } 214 | 215 | 216 | return ( 217 | 218 |
223 | {createNode()} 224 | 225 | 226 |
227 |
228 | ); 229 | } 230 | 231 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/QueryInputNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, NodeProps, Position } from 'reactflow'; 3 | import { Input, Card, Dropdown, Divider } from 'antd'; 4 | 5 | import { createCardTitle, SelectInput, createDebug, createTextArea, nodeStyle, getI18n } from "./Base" 6 | 7 | import i18n from "i18next"; 8 | 9 | 10 | const createUrl = (input: string, json: any, onChange: any) => { 11 | 12 | const { queryObj, userInput, nodeInputId } = json; 13 | const { query, action } = queryObj; 14 | 15 | let nodeOpts: any[] = []; 16 | if (json.getNodes) nodeOpts = [...json.getNodes(json.id)] 17 | let selectNodeValue = input === "nodeInput" ? (nodeInputId || nodeOpts[0].value) : null 18 | // console.log('createUrl',input, selectNodeValue,nodeOpts) 19 | return
{ 20 | onChange({ 21 | key: 'draggable', 22 | data: false 23 | }) 24 | }} 25 | onMouseLeave={() => { 26 | onChange({ 27 | key: 'draggable', 28 | data: true 29 | }) 30 | }}> 31 | 32 | { 33 | createTextArea(i18n.t('selectQuery'), query, ".tag", "", (e: any) => { 34 | const data = { 35 | ...json, 36 | queryObj: { 37 | ...json.queryObj, 38 | content: '', 39 | query: e.data, 40 | action: 'input' 41 | } 42 | } 43 | onChange({ 44 | key: 'data', 45 | data 46 | }) 47 | }) 48 | } 49 | 50 | 51 |

{i18n.t('queryInputText')}

52 | 60 | 61 | 62 |
63 | } 64 | 65 | 66 | 67 | function Main({ id, data, selected }: any) { 68 | // i18nInit(); 69 | const { debugMenu, contextMenus } = getI18n(); 70 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 71 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 72 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 73 | 74 | const [queryObj, setQueryObj] = React.useState(data.queryObj); 75 | 76 | const [input, setInput] = React.useState(data.input); 77 | 78 | // 更新数据 79 | const updateData = (e: any) => { 80 | 81 | if (e.key === 'data') { 82 | if (e.data.queryObj) setQueryObj(e.data.queryObj); 83 | data.onChange({ id, data: { ...data, ...e.data } }) 84 | } 85 | 86 | 87 | if (e.key === "setInput") { 88 | setInput(e.data); 89 | const d: any = { 90 | ...data, 91 | input: e.data 92 | }; 93 | if (e.data == 'userInput' && e.userInput) { 94 | d.userInput = e.userInput; 95 | }; 96 | data.onChange({ 97 | id, 98 | data: d 99 | }) 100 | } 101 | 102 | 103 | if (e.key == 'userInput') { 104 | const d: any = { 105 | ...data, 106 | userInput: e.data 107 | }; 108 | data.onChange({ 109 | id, 110 | data: d 111 | }) 112 | }; 113 | 114 | 115 | if (e.key === "nodeInput") { 116 | data.onChange({ 117 | id, 118 | data: { 119 | ...data, 120 | nodeInputId: e.data 121 | } 122 | }) 123 | } 124 | if (e.key == "debug") data.onChange({ id, data: e.data }) 125 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 126 | } 127 | 128 | 129 | const createNode = () => { 130 | 131 | 132 | if (data.debugInput != debugInput && shouldRefresh) { 133 | setDebugInput(data.debugInput); 134 | setShouldRefresh(false) 135 | } 136 | const node = [ 137 | createUrl( 138 | input, 139 | data, 140 | updateData) 141 | ]; 142 | 143 | 144 | // node.push( 145 | // createDebug(debugMenu, id, 146 | // debugInput, 147 | // data.debugOutput, 148 | // (event: any) => { 149 | // if (event.key == 'input') { 150 | // const { data } = event; 151 | // setDebugInput(data) 152 | // let json: any; 153 | // try { 154 | // json = JSON.parse(data); 155 | // setStatusInputForDebug('') 156 | // } catch (error) { 157 | // setStatusInputForDebug('error') 158 | // } 159 | // }; 160 | // if (event.key == 'draggable') updateData(event) 161 | // }, 162 | // () => { 163 | // console.log('debugFun debugInput', debugInput) 164 | // if (debugInput != "" && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 165 | // let merged; 166 | // try { 167 | // merged = JSON.parse(debugInput) 168 | // } catch (error) { 169 | 170 | // } 171 | // console.log('debugFun merged', merged) 172 | // data.merged = merged; 173 | // data.debugInput = JSON.stringify(merged, null, 2); 174 | // if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 175 | // data.debug && data.debug(data); 176 | // } else if (debugInput == "" || debugInput.replace(/\s/ig, "") == "[]") { 177 | // data.merged = null; 178 | // data.debugInput = ""; 179 | // if (data.role) data.role.merged = null; 180 | // console.log('debugFun no merged', data) 181 | // data.debug && data.debug(data) 182 | // setShouldRefresh(true); 183 | // } else if (debugInput === undefined) { 184 | // data.debug && data.debug(data) 185 | // } 186 | // }, 187 | // () => data.merge && data.merge(data), 188 | // { 189 | // statusInput: statusInputForDebug, 190 | // statusOutput: "" 191 | // }) 192 | // ) 193 | 194 | return 201 | {...node} 202 | 203 | } 204 | 205 | 206 | return ( 207 | 208 |
212 | {createNode()} 213 | 214 | 215 |
216 |
217 | ); 218 | } 219 | 220 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/QueryReadNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | 5 | import { createCardTitle, createDebug, createSelect, createTextArea, nodeStyle, getI18n } from './Base' 6 | 7 | import i18n from "i18next"; 8 | 9 | 10 | const createUrl = (title1: string, title2: string, placeholder2: string, json: any, onChange: any) => { 11 | const { query, content } = json; 12 | // console.log("selection", content); 13 | const key = 'query' 14 | return
{ 15 | onChange({ 16 | key: 'draggable', 17 | data: false 18 | }) 19 | }} 20 | onMouseLeave={() => { 21 | onChange({ 22 | key: 'draggable', 23 | data: true 24 | }) 25 | }}> 26 | 27 | { 28 | createSelect(title1, content || "bindCurrentPage", [ 29 | { value: 'bindCurrentPageHTML', label: i18n.t('bindWebHTML') }, 30 | { value: 'bindCurrentPage', label: i18n.t('bindWebContent') }, 31 | { value: 'bindCurrentPageImages', label: i18n.t('bindWebImages') }, 32 | { value: 'bindCurrentPageURL', label: i18n.t('bindWebURL') }, 33 | { value: 'bindCurrentPageTitle', label: i18n.t('bindWebTitle') }, 34 | 35 | ], (e: any) => { 36 | 37 | if (e.key == title1) { 38 | // console.log(e) 39 | const data = { 40 | ...json, 41 | content: e.data, 42 | action: 'read' 43 | } 44 | 45 | onChange({ 46 | key: "query", 47 | data 48 | }) 49 | } 50 | 51 | }) 52 | } 53 | 54 | { 55 | content === 'bindCurrentPageHTML' || content === 'bindCurrentPage' || content === 'bindCurrentPageImages' ? 56 | createTextArea(title2, query, placeholder2, "", (e: any) => { 57 | const data = { 58 | ...json, 59 | query: e.data, 60 | action: 'read' 61 | } 62 | 63 | onChange({ 64 | key: "query", 65 | data 66 | }) 67 | }) : null 68 | } 69 | { 70 | content === 'bindCurrentPageHTML' || content === 'bindCurrentPage' || content === 'bindCurrentPageImages' ? 71 |

{i18n.t('queryReadPlaceholderTips')}

: null 72 | } 73 | 74 |
75 | } 76 | 77 | 78 | 79 | function QueryReadNode({ id, data, selected }: any) { 80 | // i18nInit(); 81 | const { debugMenu, contextMenus } = getI18n(); 82 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 83 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 84 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 85 | 86 | const [queryObj, setQueryObj] = React.useState(data.queryObj) 87 | const updateData = (e: any) => { 88 | // console.log(e) 89 | if (e.key === 'query') { 90 | setQueryObj(e.data); 91 | data.onChange({ id, data: { queryObj: e.data } }) 92 | } 93 | if (e.key == "debug") data.onChange({ id, data: e.data }) 94 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 95 | } 96 | 97 | const createNode = () => { 98 | 99 | if (data.debugInput != debugInput && shouldRefresh) { 100 | setDebugInput(data.debugInput); 101 | setShouldRefresh(false) 102 | } 103 | 104 | const node = [ 105 | createUrl(i18n.t('content'), i18n.t('selectQuery'), i18n.t('queryReadPlaceholder'), queryObj, updateData) 106 | ]; 107 | 108 | // node.push( 109 | // createDebug(debugMenu, id, 110 | // debugInput, 111 | // data.debugOutput, 112 | // (event: any) => { 113 | // if (event.key == 'input') { 114 | // const { data } = event; 115 | // setDebugInput(data) 116 | // let json: any; 117 | // try { 118 | // json = JSON.parse(data); 119 | // setStatusInputForDebug('') 120 | // } catch (error) { 121 | // setStatusInputForDebug('error') 122 | // } 123 | // }; 124 | // if (event.key == 'draggable') updateData(event) 125 | // }, 126 | // () => { 127 | // console.log('debugFun debugInput', debugInput) 128 | // if (debugInput != "" && debugInput && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 129 | // let merged; 130 | // try { 131 | // merged = JSON.parse(debugInput) 132 | // } catch (error) { 133 | 134 | // } 135 | // console.log('debugFun merged', merged) 136 | // data.merged = merged; 137 | // data.debugInput = JSON.stringify(merged, null, 2); 138 | // if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 139 | // data.debug && data.debug(data); 140 | // } else if (debugInput == "" || debugInput && debugInput.replace(/\s/ig, "") == "[]") { 141 | // data.merged = null; 142 | // data.debugInput = ""; 143 | // if (data.role) data.role.merged = null; 144 | // console.log('debugFun no merged', data) 145 | // data.debug && data.debug(data) 146 | // setShouldRefresh(true); 147 | // } else if (debugInput === undefined) { 148 | // data.debug && data.debug(data); 149 | // setShouldRefresh(true); 150 | // } 151 | // }, 152 | // () => data.merge && data.merge(data), 153 | // { 154 | // statusInput: statusInputForDebug, 155 | // statusOutput: "" 156 | // }) 157 | // ) 158 | 159 | return 164 | {...node} 165 | 166 | } 167 | 168 | 169 | return ( 170 | 171 |
175 | {createNode()} 176 | 177 | 178 |
179 |
180 | ); 181 | } 182 | 183 | export default QueryReadNode; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/RoleNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, NodeProps, Position } from 'reactflow'; 3 | import { Input, Avatar, Card, Select, Radio, InputNumber, Dropdown, Space, Button, Divider, MenuProps } from 'antd'; 4 | import { DownOutlined, UserOutlined } from '@ant-design/icons'; 5 | 6 | import { createCardTitle, createDebug, createText, nodeStyle, getI18n } from './Base' 7 | 8 | import i18n from "i18next"; 9 | // import { i18nInit } from '../i18nConfig'; 10 | import { roleAvatars } from '../Workflow' 11 | 12 | 13 | function Main({ id, data, selected }: any) { 14 | 15 | // i18nInit(); 16 | const { debugMenu, contextMenus } = getI18n(); 17 | const [statusInputForDebug, setStatusInputForDebug] = React.useState(''); 18 | const [debugInput, setDebugInput] = React.useState((data.merged ? JSON.stringify(data.merged, null, 2) : "")); 19 | const [shouldRefresh, setShouldRefresh] = React.useState(true) 20 | 21 | // text 22 | const [role, setRole] = React.useState(data.role); 23 | 24 | const updateData = (e: any) => { 25 | // console.log(e) 26 | let r = { ...role }; 27 | if (e.key == 'text' || e.key == 'name' || e.key == 'avatar') { 28 | r[e.key] = e.data; 29 | setRole({ 30 | ...r 31 | }) 32 | data.onChange({ id, data: { role: r, debugInput: "" } }); 33 | 34 | setShouldRefresh(true); 35 | 36 | }; 37 | 38 | if (e.key == "debug") data.onChange({ id, data: e.data }) 39 | 40 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }) 41 | } 42 | 43 | 44 | const createNode = (role: any, updateData: any) => { 45 | // const items: any = Array.from(roleAvatars, (avatar: any) => { 46 | // return { 47 | // label: avatar.label, 48 | // key: avatar.key, 49 | // icon: , 50 | // disabled: !!avatar.disabled, 51 | // checked: avatar.key === role.avatar 52 | // } 53 | // }); 54 | 55 | // const handleMenuClick: any = (e: any) => { 56 | // // console.log('roleAvatars click', e); 57 | // updateData({ 58 | // key: 'avatar', data: e.key 59 | // }) 60 | // }; 61 | 62 | // console.log(data.debugInput, debugInput) 63 | if (data.debugInput != debugInput && shouldRefresh) { 64 | setDebugInput(data.debugInput); 65 | setShouldRefresh(false) 66 | } 67 | 68 | return 76 | 77 | { 78 | createText('text', i18n.t('createRole'), i18n.t('inputTextPlaceholder'), role.text, '', updateData) 79 | } 80 | 81 | { 82 | createDebug(debugMenu, id, 83 | debugInput, 84 | data.debugOutput, 85 | (event: any) => { 86 | if (event.key == 'input') { 87 | const { data } = event; 88 | setDebugInput(data) 89 | let json: any; 90 | try { 91 | json = JSON.parse(data); 92 | setStatusInputForDebug('') 93 | } catch (error) { 94 | setStatusInputForDebug('error') 95 | } 96 | }; 97 | if (event.key == 'draggable') updateData(event) 98 | }, 99 | () => { 100 | console.log('debugFun debugInput', debugInput) 101 | if (debugInput != "" && debugInput && debugInput.replace(/\s/ig, "") != "[]" && statusInputForDebug != 'error') { 102 | let merged; 103 | try { 104 | merged = JSON.parse(debugInput) 105 | } catch (error) { 106 | 107 | } 108 | console.log('debugFun merged', merged) 109 | data.merged = merged; 110 | data.debugInput = JSON.stringify(merged, null, 2); 111 | if (data.role) data.role.merged = merged.filter((f: any) => f.role == 'system'); 112 | data.debug && data.debug(data); 113 | } else if (debugInput == "" || debugInput && debugInput.replace(/\s/ig, "") == "[]") { 114 | data.merged = null; 115 | data.debugInput = ""; 116 | if (data.role) data.role.merged = null; 117 | console.log('debugFun no merged', data) 118 | data.debug && data.debug(data) 119 | setShouldRefresh(true); 120 | } else if (debugInput === undefined) { 121 | data.debug && data.debug(data); 122 | setShouldRefresh(true); 123 | } 124 | }, 125 | () => data.merge && data.merge(data), 126 | { 127 | statusInput: statusInputForDebug, 128 | statusOutput: "" 129 | }) 130 | } 131 | 132 | 133 | } 134 | 135 | 136 | return ( 137 | 140 |
144 | 145 | {createNode(role, updateData)} 146 | {/* */} 147 | 148 | 149 | 150 |
151 |
152 | ); 153 | } 154 | 155 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/UserInputTextNode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Handle, Position } from 'reactflow'; 3 | import { Card, Dropdown } from 'antd'; 4 | import i18n from "i18next"; 5 | import { createCardTitle, nodeStyle, getI18n, createText } from './Base'; 6 | 7 | function Main({ id, data, selected }: any) { 8 | // i18nInit(); 9 | const { contextMenus } = getI18n(); 10 | 11 | const [userInput, setUserInput] = React.useState(data.userInput) 12 | 13 | const updateData = (e: any) => { 14 | 15 | if (e.key == 'draggable') data.onChange({ id, data: { draggable: e.data } }); 16 | 17 | if (e.key == 'userInput') { 18 | setUserInput(e.data); 19 | data.onChange({ id, data: { userInput: e.data } }); 20 | } 21 | } 22 | 23 | const createNode = () => { 24 | 25 | let nodeOpts: any[] = []; 26 | if (data.getNodes) nodeOpts = [...data.getNodes(id)] 27 | 28 | return 33 | {/*

{i18n.t('queryInputNodeTitle')}

*/} 34 | { 35 | createText('userInput', i18n.t('userInputTextTip'), '...', userInput, '', updateData) 36 | } 37 |
38 | } 39 | 40 | return ( 41 | 42 |
47 | {createNode()} 48 | 49 | 50 |
51 |
52 | ); 53 | } 54 | 55 | export default Main; -------------------------------------------------------------------------------- /src/components/flow/nodeComponents/index.tsx: -------------------------------------------------------------------------------- 1 | import QueryDefaultNode from './QueryDefaultNode'; 2 | import QueryClickNode from './QueryClickNode' 3 | import QueryReadNode from './QueryReadNode'; 4 | import QueryInputNode from './QueryInputNode'; 5 | import APINode from './APINode'; 6 | import RoleNode from './RoleNode'; 7 | import PromptNode from './PromptNode'; 8 | import CustomPromptNode from './CustomPromptNode'; 9 | import UserInputTextNode from './UserInputTextNode' 10 | import FilePPTCreateNode from './FilePPTCreateNode' 11 | import InputMergeNode from './InputMergeNode' 12 | 13 | import i18n from "i18next"; 14 | 15 | 16 | const getNodes = () => [{ 17 | key: 'role', 18 | title: i18n.t('roleNodeTitle'), 19 | children: [ 20 | { 21 | key: 'role', 22 | component: RoleNode, 23 | parent: 'role', 24 | name: i18n.t('createRole'), 25 | disabled: true 26 | } 27 | ] 28 | }, 29 | { 30 | key: 'llm', 31 | title: i18n.t('llm'), 32 | children: [{ 33 | key: 'prompt', 34 | component: PromptNode, 35 | parent: 'llm', 36 | name: i18n.t('promptNodeTitle') 37 | }, { 38 | key: 'promptCustom', 39 | component: CustomPromptNode, 40 | parent: 'llm', 41 | name: i18n.t('customPromptNodeTitle') 42 | } 43 | // { 44 | // key: 'embeddings', 45 | // component: EmbeddingsNode, 46 | // parent: 'llm', 47 | // name: i18n.t('embeddingsNodeTitle') 48 | // }, 49 | ], 50 | open: true 51 | }, 52 | { 53 | key: 'input', 54 | title: i18n.t('inputMenu'), 55 | children: [{ 56 | key: 'userInputText', 57 | component: UserInputTextNode, 58 | parent: 'input', 59 | name: i18n.t('userInputText') 60 | }, 61 | { 62 | key: 'inputMerge', 63 | component: InputMergeNode, 64 | parent: 'input', 65 | name: i18n.t('inputMerge') 66 | } 67 | ], 68 | }, 69 | { 70 | key: 'query', 71 | title: i18n.t('webAgent'), 72 | children: [ 73 | { 74 | key: 'queryDefault', 75 | component: QueryDefaultNode, 76 | parent: 'query', 77 | name: i18n.t('queryDefaultNodeTitle') 78 | }, 79 | { 80 | key: 'queryClick', 81 | component: QueryClickNode, 82 | parent: 'query', 83 | name: i18n.t('queryClickNodeTitle') 84 | }, 85 | // { 86 | // key: 'queryScroll', 87 | // component: QueryScrollNode, 88 | // parent: 'query', 89 | // name: i18n.t('queryScrollNodeTitle') 90 | // }, 91 | { 92 | key: 'queryRead', 93 | component: QueryReadNode, 94 | parent: 'query', 95 | name: i18n.t('queryReadNodeTitle') 96 | }, 97 | { 98 | key: 'queryInput', 99 | component: QueryInputNode, 100 | parent: 'query', 101 | name: i18n.t('queryInputNodeTitle') 102 | }, 103 | ] 104 | 105 | }, 106 | { 107 | key: 'api', 108 | title: 'API', 109 | children: [ 110 | { 111 | key: 'api', 112 | component: APINode, 113 | parent: 'api', 114 | name: i18n.t('apiNodeTitle') 115 | } 116 | ] 117 | }, 118 | { 119 | key: 'file', 120 | title: 'File', 121 | children: [ 122 | { 123 | key: 'file-ppt-create', 124 | component: FilePPTCreateNode, 125 | parent: 'file', 126 | name: i18n.t('filePPTNodeTitle') 127 | } 128 | ] 129 | } 130 | ] 131 | 132 | 133 | export default getNodes; 134 | -------------------------------------------------------------------------------- /src/components/runtime/api.tsx: -------------------------------------------------------------------------------- 1 | export function ApiRun(prompt: any, combo: any) { 2 | 3 | let { url, init, protocol } = prompt.api; 4 | 5 | if (url && !url.match('//')) url = `${protocol}${url}`; 6 | // console.log(api, init.body) 7 | 8 | if (init.body && typeof (init.body) == 'object') init.body = JSON.stringify(init.body); 9 | 10 | let prePromptText: any = ""; 11 | if (prompt.input == "nodeInput") { 12 | prePromptText = prompt.context; 13 | } 14 | 15 | if (init.body && typeof (init.body) == 'string' && prePromptText) { 16 | // 替换${context} 表示从上一个节点传递来的text 17 | // prePromptText = prePromptText.replaceAll('"', '') 18 | // prePromptText = prePromptText.replaceAll("'", '') 19 | // prePromptText = prePromptText.replace(/\n/ig, '') 20 | init.body = init.body.replaceAll('${context}', prePromptText) 21 | } 22 | 23 | return { 24 | url, init, combo, promptId: prompt.id, prePromptText 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/components/runtime/llm.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | promptBindCurrentSite, 3 | promptBindUserSelection, 4 | promptBindTasks, 5 | promptBindUserClipboard, 6 | userSelectionInit, 7 | extractDomElement, 8 | promptParse, 9 | promptUseLastTalk, 10 | promptBindRole, 11 | bindUserInput, 12 | promptBindTranslate, 13 | promptBindOutput 14 | } from '@src/components/combo/Prompt' 15 | 16 | function _temperature2BingStyle(temperature = 0.6) { 17 | let style = 'Balanced'; 18 | if (temperature < 0.3) style = 'Creative' 19 | if (temperature > 0.7) style = 'Precise' 20 | } 21 | 22 | 23 | export function LLMRun(prompt: any, newTalk: boolean) { 24 | // console.log('this.state.chatBotStyle', this.state.chatBotStyle) 25 | const { temperature, model, text, type, merged, role } = prompt; 26 | 27 | let promptData; 28 | 29 | if (merged && merged.length > 0) { 30 | // 使用合成好的prompt 31 | promptData = merged; 32 | promptData = Array.from(promptData, (p: any) => { 33 | if (p.role == 'user' && prompt['context']) { 34 | p.content = p.content.replaceAll("${context}", prompt['context']) 35 | } 36 | if (p.role == 'system' && prompt['context']) { 37 | p.content = p.content.replaceAll("${context}", prompt['context']) 38 | } 39 | return p 40 | }) 41 | } else { 42 | const { system, user, assistant } = promptParse(prompt); 43 | promptData = [system, user]; 44 | } 45 | 46 | 47 | // role 合成好的处理 48 | if (role && role.merged && role.merged[0]) { 49 | promptData = Array.from(promptData, (p: any) => { 50 | if (p.role == 'system') { 51 | p = role.merged[0] 52 | } 53 | return p 54 | }) 55 | } 56 | 57 | 58 | let chatBotType = model, 59 | style: any = temperature; 60 | 61 | 62 | // 增加一个Bing的转化 63 | if (model == "Bing" && typeof (temperature) == 'number' && temperature > -1) style = _temperature2BingStyle(temperature); 64 | 65 | 66 | console.log(`sendMessageToBackground['chat-bot-talk']`, style, chatBotType, promptData) 67 | 68 | const data = { 69 | prompt: promptData, 70 | type: chatBotType, 71 | style, 72 | newTalk: !!newTalk 73 | } 74 | return data 75 | 76 | } -------------------------------------------------------------------------------- /src/components/runtime/webAgent.tsx: -------------------------------------------------------------------------------- 1 | import { md5 } from '@components/Utils' 2 | import { 3 | promptBindCurrentSite, 4 | promptBindUserSelection, 5 | promptBindTasks, 6 | promptBindUserClipboard, 7 | userSelectionInit, 8 | extractDomElement, 9 | promptParse, 10 | promptUseLastTalk, 11 | promptBindRole, 12 | bindUserInput, 13 | promptBindTranslate, 14 | promptBindOutput 15 | } from '@components/combo/Prompt' 16 | import i18n from 'i18next'; 17 | 18 | 19 | 20 | function addHighlight(dom: any) { 21 | if (dom) dom.style.border = "1px solid yellow" 22 | } 23 | 24 | 25 | function inputByQueryBase(query: string, text: string) { 26 | const inp: any = document.querySelector(query); 27 | if (inp && text) { 28 | inp.innerText = text; 29 | inp.value = text; 30 | inp.focus() 31 | inp.select() 32 | document.execCommand("insertText", false, text) 33 | addHighlight(inp) 34 | } 35 | 36 | } 37 | 38 | function clickByQueryBase(query: string) { 39 | const dom: any = document.querySelector(query); 40 | if (dom) { 41 | dom.click(); 42 | addHighlight(dom) 43 | } 44 | } 45 | 46 | export function QueryDefaultRun(queryObj: any, prePromptText: string, combo: any) { 47 | return new Promise((res, rej) => { 48 | let result = {} 49 | 50 | if (queryObj) { 51 | // 如果是query,则开始调用网页代理 ,&& 避免代理页面也发起了新的agent 52 | let { 53 | url, protocol, delay 54 | } = queryObj; 55 | 56 | if (delay === 0) delay = 1; 57 | 58 | 59 | if (url) { 60 | // ${context} 61 | if (url.match(/\${context}/) && prePromptText) { 62 | url = url.replaceAll('${context}', prePromptText) 63 | } 64 | // 对url进行处理 65 | if (url && !url.match('//')) url = `${protocol}${url}`; 66 | 67 | const data = JSON.parse(JSON.stringify({ 68 | type: 'queryDefault', 69 | url, 70 | delay: delay || 2000, 71 | combo: { ...combo } //用来传递combo数据 72 | })); 73 | // console.log('_queryDefaultRun', data) 74 | 75 | res({ 76 | from: '_queryDefaultRun', 77 | url, 78 | data 79 | }) 80 | } else { 81 | // url没有填写 82 | let id = md5("_queryDefaultRun" + (new Date())) 83 | setTimeout(() => { 84 | res({ 85 | from: '_queryDefaultRun', 86 | id: id + 'r', 87 | text: '延时' + delay + '毫秒', 88 | delay 89 | }) 90 | }, delay); 91 | } 92 | 93 | } 94 | 95 | }) 96 | 97 | } 98 | 99 | export function QueryInputRun(prompt: any, delay = 1000) { 100 | return new Promise((res, rej) => { 101 | let text: any = prompt.context || '', 102 | query = prompt.queryObj.query; 103 | setTimeout(() => { 104 | // 当前页面 105 | inputByQueryBase(query, text) 106 | const id = md5(query + text + (new Date())) 107 | res({ 108 | from: '_queryInputRun', 109 | id, 110 | text: '文本输入:' + text 111 | }) 112 | }, delay) 113 | }) 114 | } 115 | 116 | export function QueryClickRun(prompt: any, delay = 1000) { 117 | return new Promise((res, rej) => { 118 | let query = prompt.queryObj.query; 119 | // 当前页面 120 | setTimeout(() => { 121 | clickByQueryBase(query) 122 | const id = md5(query + (new Date())); 123 | 124 | res({ 125 | from: '_queryClickRun', 126 | id, 127 | text: '模拟点击' 128 | }) 129 | 130 | }, delay) 131 | }) 132 | 133 | } 134 | 135 | export function QueryReadRun(queryObj: any) { 136 | return new Promise((res, rej) => { 137 | const { content, query, url, protocol } = queryObj; 138 | // console.log('_queryReadRun', queryObj) 139 | let prompt = {}; 140 | if (content == 'bindCurrentPage') { 141 | // 绑定全文 142 | prompt = promptBindCurrentSite('', 'text', query) 143 | } else if (content == 'bindCurrentPageHTML') { 144 | // 绑定网页 145 | prompt = promptBindCurrentSite('', 'html', query) 146 | } else if (content == 'bindCurrentPageURL') { 147 | // 绑定url 148 | prompt = promptBindCurrentSite('', 'url', query) 149 | } else if (content == 'bindCurrentPageImages') { 150 | prompt = promptBindCurrentSite('', 'images', query) 151 | } else if (content == "bindCurrentPageTitle") { 152 | prompt = promptBindCurrentSite('', 'title', query) 153 | } 154 | 155 | const { id, system, user } = promptParse(prompt); 156 | 157 | // 折叠的样式实现 158 | const markdown = `
159 | ${i18n.t("queryReadRunResult")} 160 |

${user.content}

161 |
`; 162 | 163 | setTimeout(() => res({ 164 | from: '_queryReadRun', 165 | id: id + 'r', 166 | markdown, 167 | data: { 168 | content, 169 | query, 170 | protocol, 171 | url 172 | } 173 | }), 500); 174 | }) 175 | 176 | } 177 | 178 | // then(res => { 179 | // const data = Talks.createTaskStatus( 180 | // '_queryScrollRun', 181 | // id, 182 | // '模拟滚动' 183 | // ) 184 | // this._updateChatBotTalksResult([data]); 185 | // }) 186 | 187 | -------------------------------------------------------------------------------- /src/config/app.json: -------------------------------------------------------------------------------- 1 | {"app":"Earth","version":"0.3.8.0","browsers":["chrome"],"discord":"https://discord.gg/SGwA9anUrr","issues":"https://discord.gg/VtRmJCb8wh","chatGPT":{"token":"","apis":[{"value":"https://api.openai.com","label":"openai"}],"api":"https://api.openai.com","displayApiName":"openai","models":["gpt-3.5-turbo"],"model":"gpt-3.5-turbo","canImport":true},"dev":true} 2 | -------------------------------------------------------------------------------- /src/config/commonsConfig.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "tag": "textSummary", 3 | "role": {}, 4 | "combo": 2, 5 | "interfaces": [ 6 | "contextMenus" 7 | ], 8 | "isInfinite": false, 9 | "owner": "user", 10 | "prompt": { 11 | "id": "QUERYREAD_F7CNP2-MJNWA5UTLFB9RT", 12 | "nextId": "PROMPT_UJ_9TH76A-XJWGYELSWFC", 13 | "nodeInputId": "", 14 | "role": {}, 15 | "text": "", 16 | "url": "", 17 | "queryObj": { 18 | "action": "default", 19 | "content": "bindCurrentPage", 20 | "protocol": "https://", 21 | "query": "", 22 | "url": "" 23 | }, 24 | "temperature": 0.6, 25 | "model": "ChatGPT", 26 | "input": "nodeInput", 27 | "userInput": "", 28 | "translate": "default", 29 | "output": "default", 30 | "type": "queryRead" 31 | }, 32 | "version": "0.1.0", 33 | "app": "brainwave", 34 | "id": "ERYREAD_F7CNP2-MJNWA5UTLFB9", 35 | "createDate": 1686642591374, 36 | "prompt2": { 37 | "id": "PROMPT_UJ_9TH76A-XJWGYELSWFC", 38 | "nextId": "", 39 | "nodeInputId": "QUERYREAD_F7CNP2-MJNWA5UTLFB9RT", 40 | "role": {}, 41 | "text": "请帮我用中文完成以下任务的输出,它们分别是总结摘要和要点,你需要用以下格式输出:\\n<摘要>\\nxxxx\\n<要点>\\n1.xxxx\\n2.xxxx", 42 | "url": "", 43 | "temperature": 0.7, 44 | "model": "ChatGPT", 45 | "input": "nodeInput", 46 | "userInput": "", 47 | "translate": "default", 48 | "output": "default", 49 | "type": "prompt" 50 | } 51 | }, 52 | { 53 | "tag": "keyKnowledgeIntro", 54 | "role": { 55 | "name": "", 56 | "text": "" 57 | }, 58 | "combo": 2, 59 | "interfaces": [ 60 | "contextMenus" 61 | ], 62 | "isInfinite": false, 63 | "owner": "user", 64 | "prompt": { 65 | "id": "QUERYREAD_JNAZ_TBFWGX2Z7RT6A6Z4", 66 | "nextId": "PROMPT_ZIDLTEGVIPTWPR6C5LNRG", 67 | "nodeInputId": "", 68 | "role": { 69 | "name": "", 70 | "text": "" 71 | }, 72 | "text": "", 73 | "url": "", 74 | "queryObj": { 75 | "action": "default", 76 | "content": "bindCurrentPage", 77 | "protocol": "https://", 78 | "query": "", 79 | "url": "" 80 | }, 81 | "temperature": 0.6, 82 | "model": "ChatGPT", 83 | "input": "nodeInput", 84 | "userInput": "", 85 | "translate": "default", 86 | "output": "default", 87 | "type": "queryRead" 88 | }, 89 | "version": "0.1.0", 90 | "app": "brainwave", 91 | "id": "fSCM4acIZn_VvIiwMLcvW", 92 | "createDate": 1686642923375, 93 | "prompt2": { 94 | "id": "PROMPT_ZIDLTEGVIPTWPR6C5LNRG", 95 | "nextId": "", 96 | "nodeInputId": "QUERYREAD_JNAZ_TBFWGX2Z7RT6A6Z4", 97 | "role": { 98 | "name": "", 99 | "text": "" 100 | }, 101 | "text": "请找出文章中出现频率最多的10个关键词,并用中文解释这些关键词是什么意思。", 102 | "url": "", 103 | "temperature": 0.48, 104 | "model": "ChatGPT", 105 | "input": "nodeInput", 106 | "userInput": "", 107 | "translate": "translate-zh", 108 | "output": "default", 109 | "type": "prompt" 110 | } 111 | } 112 | ] -------------------------------------------------------------------------------- /src/config/editableConfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /src/config/selectionConfig.json: -------------------------------------------------------------------------------- 1 | [ { 2 | "tag": "introduceMore", 3 | "role": { 4 | "name": "", 5 | "text": "", 6 | "merged": [] 7 | }, 8 | "combo": 1, 9 | "interfaces": [ 10 | "contextMenus-selection" 11 | ], 12 | "isInfinite": false, 13 | "owner": "user", 14 | "prompt": { 15 | "id": "PROMPT_E7NCJO7EB1UJYPD60YITL", 16 | "nextId": "", 17 | "nodeInputId": "", 18 | "role": { 19 | "name": "", 20 | "text": "", 21 | "merged": [] 22 | }, 23 | "text": "帮我找到相关内容中的核心信息,并查询相关百科介绍和相关资讯的链接,你需要用以下格式输出:\\n<百科>\\nxxxx\\n<相关资讯>\\n1.xxxx\\n2.xxxx", 24 | "url": "", 25 | "temperature": 0.6, 26 | "model": "Bing", 27 | "input": "nodeInput", 28 | "userInput": "", 29 | "translate": "default", 30 | "output": "default", 31 | "type": "prompt", 32 | "merged": [] 33 | }, 34 | "version": "0.1.0", 35 | "app": "brainwave", 36 | "id": "cd46Rr9s95Kk7as-0SlBF", 37 | "createDate": 1687225769848 38 | }, 39 | { 40 | "tag": "contentExpansion", 41 | "role": { 42 | "name": "", 43 | "text": "", 44 | "merged": [] 45 | }, 46 | "combo": 1, 47 | "interfaces": [ 48 | "contextMenus-selection" 49 | ], 50 | "isInfinite": false, 51 | "owner": "user", 52 | "prompt": { 53 | "id": "PROMPT_KLB_NC08ZUGODHAUGPQSN", 54 | "nextId": "", 55 | "nodeInputId": "", 56 | "role": { 57 | "name": "", 58 | "text": "", 59 | "merged": [] 60 | }, 61 | "text": "帮我将相关内容进行扩写,重点内容不能发生变化。", 62 | "url": "", 63 | "temperature": 0.6, 64 | "model": "ChatGPT", 65 | "input": "nodeInput", 66 | "userInput": "", 67 | "translate": "default", 68 | "output": "default", 69 | "type": "prompt", 70 | "merged": [] 71 | }, 72 | "version": "0.1.0", 73 | "app": "brainwave", 74 | "id": "fG9SoCEwyxwcy2VvHvMqx", 75 | "createDate": 1687226065682 76 | }, 77 | { 78 | "tag": "abbreviations", 79 | "role": { 80 | "merged": [], 81 | "name": "", 82 | "text": "" 83 | }, 84 | "combo": 1, 85 | "interfaces": [ 86 | "contextMenus-selection" 87 | ], 88 | "isInfinite": false, 89 | "owner": "user", 90 | "prompt": { 91 | "id": "PROMPT_DYCMLQHH_Z3ADQCTL-7FA", 92 | "nextId": "", 93 | "nodeInputId": "", 94 | "role": { 95 | "merged": [], 96 | "name": "", 97 | "text": "" 98 | }, 99 | "text": "帮我将相关内容进行缩写,重点内容不能发生变化。", 100 | "url": "", 101 | "temperature": 0.6, 102 | "model": "ChatGPT", 103 | "input": "nodeInput", 104 | "userInput": "", 105 | "translate": "default", 106 | "output": "default", 107 | "type": "prompt", 108 | "merged": [] 109 | }, 110 | "version": "0.1.0", 111 | "app": "brainwave", 112 | "id": "-N888z9tqyp8Dk5AyRDzY", 113 | "createDate": 1687226225217 114 | }, 115 | { 116 | "tag": "rewriteContent", 117 | "role": { 118 | "name": "", 119 | "text": "", 120 | "merged": [] 121 | }, 122 | "combo": 1, 123 | "interfaces": [ 124 | "contextMenus-selection" 125 | ], 126 | "isInfinite": false, 127 | "owner": "user", 128 | "prompt": { 129 | "id": "PROMPT_LBUF4RKKMDNGWHUGXSEOJ", 130 | "nextId": "", 131 | "nodeInputId": "", 132 | "role": { 133 | "name": "", 134 | "text": "", 135 | "merged": [] 136 | }, 137 | "text": "帮我将相关内容进行文笔优化,字数差不多,重点内容不能发生变化。", 138 | "url": "", 139 | "temperature": 0.6, 140 | "model": "ChatGPT", 141 | "input": "nodeInput", 142 | "userInput": "", 143 | "translate": "default", 144 | "output": "default", 145 | "type": "prompt", 146 | "merged": [] 147 | }, 148 | "version": "0.1.0", 149 | "app": "brainwave", 150 | "id": "WdPYAlNsh0RrzTO0PgTta", 151 | "createDate": 1687226281864 152 | } 153 | ] -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import Chrome from 'chrome'; 2 | 3 | declare namespace chrome { 4 | export default Chrome; 5 | } 6 | 7 | declare global { 8 | interface Window { 9 | clarity:any 10 | } 11 | } 12 | 13 | declare module "*.svg" { 14 | import React = require('react'); 15 | export const ReactComponent: React.SFC>; 16 | const src: string; 17 | export default src; 18 | } 19 | 20 | declare module "*.jpg" { 21 | const content: string; 22 | export default content; 23 | } 24 | 25 | declare module "*.png" { 26 | const content: string; 27 | export default content; 28 | } 29 | 30 | // declare module "*.json" { 31 | // const content: string; 32 | // export default content; 33 | // } 34 | 35 | declare module "*.json" { 36 | const value: any; 37 | export default value; 38 | } -------------------------------------------------------------------------------- /src/locales/i18nConfig.ts: -------------------------------------------------------------------------------- 1 | import i18n from "i18next"; 2 | import { initReactI18next } from "react-i18next"; 3 | import LanguageDetector from "i18next-browser-languagedetector"; 4 | 5 | import en from "./en.json"; 6 | import zh from "./zh.json"; 7 | 8 | const resources = { 9 | en: { 10 | translation: en, 11 | }, 12 | zh: { 13 | translation: zh, 14 | }, 15 | }; 16 | 17 | 18 | export const i18nInit = () => { 19 | i18n 20 | .use(LanguageDetector) 21 | .use(initReactI18next) 22 | .init({ 23 | resources, 24 | fallbackLng: "en", // 如果找不到当前语言的翻译文本,将使用该语言作为回退 25 | lng: navigator.language, 26 | debug: false, 27 | interpolation: { 28 | escapeValue: false, // 不需要对翻译文本进行转义 29 | }, 30 | }); 31 | } 32 | 33 | try { 34 | i18nInit() 35 | // console.log('i18n',i18n) 36 | } catch (error) { 37 | // setTimeout(()=>) 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/manifest/index.mts: -------------------------------------------------------------------------------- 1 | import getManifestV2 from "./v2.mjs"; 2 | import getManifestV3 from "./v3.mjs"; 3 | 4 | export function getManifest(version: 2 | 3, pageDirMap: { [x: string]: any }) { 5 | if (version === 2) { 6 | return getManifestV2(pageDirMap); 7 | } else if (version === 3) { 8 | return getManifestV3(pageDirMap); 9 | } else { 10 | throw new Error("Invalid version"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/manifest/v2-type.mts: -------------------------------------------------------------------------------- 1 | /** 2 | * rollup-plugin-chrome-extension 3 | *http://github.com/extend-chrome/rollup-plugin-chrome-extension 4 | */ 5 | export interface ManifestTypeV2 { 6 | manifest_version: 2; 7 | name: string; 8 | version: string; 9 | default_locale?: string | undefined; 10 | description?: string | undefined; 11 | icons?: chrome.runtime.ManifestIcons | undefined; 12 | author?: string | undefined; 13 | background?: { 14 | page?: string; 15 | persistent?: boolean; 16 | scripts: string[]; 17 | service_worker?: string; 18 | type?: string; 19 | }; 20 | browser_action?: { 21 | browser_style?: boolean; 22 | default_icon?: chrome.runtime.ManifestIcons | undefined; 23 | default_title?: string; 24 | default_popup?: string; 25 | theme_icons?: chrome.runtime.ManifestIcons[] | undefined; 26 | }; 27 | // chrome_settings_overrides?: boolean; 28 | // chrome_url_overrides?: { 29 | // bookmarks?: string; 30 | // history?: string; 31 | // newtab?: string; 32 | // }; 33 | commands?: 34 | | { 35 | [name: string]: { 36 | suggested_key?: 37 | | { 38 | default?: string | undefined; 39 | windows?: string | undefined; 40 | mac?: string | undefined; 41 | chromeos?: string | undefined; 42 | linux?: string | undefined; 43 | } 44 | | undefined; 45 | description?: string | undefined; 46 | }; 47 | } 48 | | undefined; 49 | content_pack?: any; 50 | content_scripts?: 51 | { 52 | matches: string[]; 53 | all_frames?: boolean; 54 | css?: string[]; 55 | exclude_globs?: string[]; 56 | exclude_matches?: string[]; 57 | include_globs?: string[]; 58 | js?: string[]; 59 | match_about_blank?: boolean; 60 | run_at?: "document_idle" | "document_start" | "document_end"; 61 | }[]; 62 | content_security_policy?: string; 63 | current_locale?: any; 64 | devtools_page?: string; 65 | externally_connectable?: { 66 | matches: string[]; 67 | accepts_tls_channel_id?: boolean; 68 | }; 69 | file_browser_handlers?: 70 | { 71 | id: string; 72 | default_title: string; 73 | file_filters: string[]; 74 | }[]; 75 | homepage_url?: string; 76 | import?: boolean; 77 | incognito?: "spanning" | "split" | "not_allowed"; 78 | input_components?: 79 | { 80 | id: string; 81 | name: string; 82 | description: string; 83 | type: "ime" | "xkb"; 84 | layouts: string[]; 85 | }[]; 86 | key?: string; 87 | // minimum_chrome_version?: string; 88 | nacl_modules?: 89 | { 90 | path: string; 91 | mime_type: "video/w+" | "audio/w+" | "application/w+"; 92 | }[]; 93 | oauth2?: { 94 | client_id?: string; 95 | scopes?: string[]; 96 | }; 97 | offline_enabled?: boolean; 98 | omnibox?: { 99 | keyword: string; 100 | }; 101 | optional_permissions?: []; 102 | options_ui?: { 103 | page: string; 104 | browser_style?: boolean; 105 | open_in_tab?: boolean; 106 | }; 107 | page_action?: { 108 | browser_style?: boolean; 109 | default_icon?: chrome.runtime.ManifestIcons | undefined; 110 | default_title?: string; 111 | default_popup?: string; 112 | theme_icons?: chrome.runtime.ManifestIcons[] | undefined; 113 | }; 114 | permissions?: chrome.runtime.ManifestPermissions[] | string[] | undefined; 115 | platforms?: 116 | | { 117 | nacl_arch?: string | undefined; 118 | sub_package_path: string; 119 | }[] 120 | | undefined; 121 | requirements?: 122 | | { 123 | "3D"?: 124 | | { 125 | features?: string[] | undefined; 126 | } 127 | | undefined; 128 | plugins?: 129 | | { 130 | npapi?: boolean | undefined; 131 | } 132 | | undefined; 133 | } 134 | | undefined; 135 | sandbox?: 136 | | { 137 | pages: string[]; 138 | content_security_policy?: string | undefined; 139 | } 140 | | undefined; 141 | short_name?: string | undefined; 142 | signature?: { 143 | key: string; 144 | signature?: string; 145 | }; 146 | spellcheck?: { 147 | dictionary_language?: string; 148 | dictionary_locale?: string; 149 | dictionary_format?: string; 150 | dictionary_path?: string; 151 | }; 152 | storage?: { 153 | managed_schema?: string; 154 | }; 155 | system_indicator?: { 156 | default_icon: chrome.runtime.ManifestIcons | undefined; 157 | default_title: string; 158 | }; 159 | tts_engine?: 160 | | { 161 | voices: { 162 | voice_name: string; 163 | lang?: string | undefined; 164 | gender?: string | undefined; 165 | event_types?: string[] | undefined; 166 | }[]; 167 | } 168 | | undefined; 169 | update_url?: string | undefined; 170 | version_name?: string | undefined; 171 | web_accessible_resources?: string[] | undefined; 172 | } 173 | -------------------------------------------------------------------------------- /src/manifest/v2.mts: -------------------------------------------------------------------------------- 1 | import { createRequire } from "node:module"; 2 | import { ManifestTypeV2 } from './v2-type.mjs'; 3 | 4 | const require = createRequire(import.meta.url); 5 | const pkg = require("../../package.json"); 6 | 7 | const manifest: ManifestTypeV2 = { 8 | manifest_version: 2, 9 | name: pkg.displayName, 10 | version: pkg.version, 11 | description: pkg.description, 12 | icons: { 13 | "128": "public/icon-128.png", 14 | }, 15 | web_accessible_resources: ["public/*", "assets/*"], 16 | }; 17 | 18 | function getManifestV2(pageDirMap: { [x: string]: any }): ManifestTypeV2 { 19 | const pages = Object.keys(pageDirMap); 20 | 21 | if (pages.length === 0) { 22 | return manifest; 23 | } 24 | 25 | if (pages.indexOf("options") > -1) { 26 | manifest.options_ui = { 27 | page: pageDirMap["options"], 28 | }; 29 | } 30 | 31 | if (pages.indexOf("background") > -1) { 32 | manifest.background = { 33 | scripts: [pageDirMap["background"]], 34 | }; 35 | } 36 | 37 | if (pages.indexOf("popup") > -1) { 38 | manifest.browser_action = { 39 | default_popup: pageDirMap["popup"], 40 | default_icon: "public/icon-34.png", 41 | }; 42 | } 43 | 44 | if (pages.indexOf("content") > -1) { 45 | manifest.content_scripts = [ 46 | { 47 | matches: ["http://*/*", "https://*/*", ""], 48 | js: [pageDirMap["content"]], 49 | css: pageDirMap["content-css"], 50 | run_at: "document_start", 51 | }, 52 | ]; 53 | } 54 | 55 | if (pages.indexOf("devtools") > -1) { 56 | manifest.devtools_page = pageDirMap["devtools"]; 57 | } 58 | 59 | return manifest; 60 | } 61 | 62 | export default getManifestV2; 63 | -------------------------------------------------------------------------------- /src/manifest/v3-type.mts: -------------------------------------------------------------------------------- 1 | /** 2 | * rollup-plugin-chrome-extension 3 | * https://github.com/extend-chrome/rollup-plugin-chrome-extension 4 | */ 5 | export interface ManifestTypeV3 { 6 | manifest_version: 3; 7 | name: string; 8 | version: string; 9 | default_locale?: string | undefined; 10 | description?: string | undefined; 11 | icons?: chrome.runtime.ManifestIcons | undefined; 12 | action?: chrome.runtime.ManifestAction | undefined; 13 | author?: string | undefined; 14 | background?: 15 | | { 16 | service_worker: string; 17 | type?: "module"; 18 | } 19 | | undefined; 20 | chrome_settings_overrides?: 21 | | { 22 | homepage?: string | undefined; 23 | search_provider?: chrome.runtime.SearchProvider | undefined; 24 | startup_pages?: string[] | undefined; 25 | } 26 | | undefined; 27 | chrome_ui_overrides?: 28 | | { 29 | bookmarks_ui?: 30 | | { 31 | remove_bookmark_shortcut?: boolean | undefined; 32 | remove_button?: boolean | undefined; 33 | } 34 | | undefined; 35 | } 36 | | undefined; 37 | chrome_url_overrides?: 38 | | { 39 | bookmarks?: string | undefined; 40 | history?: string | undefined; 41 | newtab?: string | undefined; 42 | } 43 | | undefined; 44 | commands?: 45 | | { 46 | [name: string]: { 47 | suggested_key?: 48 | | { 49 | default?: string | undefined; 50 | windows?: string | undefined; 51 | mac?: string | undefined; 52 | chromeos?: string | undefined; 53 | linux?: string | undefined; 54 | } 55 | | undefined; 56 | description?: string | undefined; 57 | global?: boolean | undefined; 58 | }; 59 | } 60 | | undefined; 61 | content_capabilities?: 62 | | { 63 | matches?: string[] | undefined; 64 | permissions?: string[] | undefined; 65 | } 66 | | undefined; 67 | content_scripts?: 68 | | { 69 | matches?: string[] | undefined; 70 | exclude_matches?: string[] | undefined; 71 | css?: string[] | undefined; 72 | js?: string[] | undefined; 73 | run_at?: string | undefined; 74 | all_frames?: boolean | undefined; 75 | match_about_blank?: boolean | undefined; 76 | include_globs?: string[] | undefined; 77 | exclude_globs?: string[] | undefined; 78 | }[] 79 | | undefined; 80 | content_security_policy?: { 81 | extension_pages?: string; 82 | sandbox?: string; 83 | }; 84 | converted_from_user_script?: boolean | undefined; 85 | current_locale?: string | undefined; 86 | declarative_net_request?: { 87 | rule_resources: DeclarativeNetRequestResource[]; 88 | }; 89 | devtools_page?: string | undefined; 90 | event_rules?: 91 | | { 92 | event?: string | undefined; 93 | actions?: 94 | | { 95 | type: string; 96 | }[] 97 | | undefined; 98 | conditions?: 99 | | chrome.declarativeContent.PageStateMatcherProperties[] 100 | | undefined; 101 | }[] 102 | | undefined; 103 | externally_connectable?: 104 | | { 105 | ids?: string[] | undefined; 106 | matches?: string[] | undefined; 107 | accepts_tls_channel_id?: boolean | undefined; 108 | } 109 | | undefined; 110 | file_browser_handlers?: 111 | | { 112 | id?: string | undefined; 113 | default_title?: string | undefined; 114 | file_filters?: string[] | undefined; 115 | }[] 116 | | undefined; 117 | file_system_provider_capabilities?: 118 | | { 119 | configurable?: boolean | undefined; 120 | watchable?: boolean | undefined; 121 | multiple_mounts?: boolean | undefined; 122 | source?: string | undefined; 123 | } 124 | | undefined; 125 | homepage_url?: string | undefined; 126 | host_permissions?: string[] | undefined; 127 | import?: 128 | | { 129 | id: string; 130 | minimum_version?: string | undefined; 131 | }[] 132 | | undefined; 133 | export?: 134 | | { 135 | whitelist?: string[] | undefined; 136 | } 137 | | undefined; 138 | incognito?: string | undefined; 139 | input_components?: 140 | | { 141 | name?: string | undefined; 142 | type?: string | undefined; 143 | id?: string | undefined; 144 | description?: string | undefined; 145 | language?: string | undefined; 146 | layouts?: string[] | undefined; 147 | }[] 148 | | undefined; 149 | key?: string | undefined; 150 | minimum_chrome_version?: string | undefined; 151 | nacl_modules?: 152 | | { 153 | path: string; 154 | mime_type: string; 155 | }[] 156 | | undefined; 157 | oauth2?: 158 | | { 159 | client_id: string; 160 | scopes?: string[] | undefined; 161 | } 162 | | undefined; 163 | offline_enabled?: boolean | undefined; 164 | omnibox?: 165 | | { 166 | keyword: string; 167 | } 168 | | undefined; 169 | optional_permissions?: 170 | | chrome.runtime.ManifestPermissions[] 171 | | string[] 172 | | undefined; 173 | options_page?: string | undefined; 174 | options_ui?: 175 | | { 176 | page?: string | undefined; 177 | chrome_style?: boolean | undefined; 178 | open_in_tab?: boolean | undefined; 179 | } 180 | | undefined; 181 | permissions?: chrome.runtime.ManifestPermissions[] | string[] | undefined; 182 | platforms?: 183 | | { 184 | nacl_arch?: string | undefined; 185 | sub_package_path: string; 186 | }[] 187 | | undefined; 188 | plugins?: 189 | | { 190 | path: string; 191 | }[] 192 | | undefined; 193 | requirements?: 194 | | { 195 | "3D"?: 196 | | { 197 | features?: string[] | undefined; 198 | } 199 | | undefined; 200 | plugins?: 201 | | { 202 | npapi?: boolean | undefined; 203 | } 204 | | undefined; 205 | } 206 | | undefined; 207 | sandbox?: 208 | | { 209 | pages: string[]; 210 | content_security_policy?: string | undefined; 211 | } 212 | | undefined; 213 | short_name?: string | undefined; 214 | spellcheck?: 215 | | { 216 | dictionary_language?: string | undefined; 217 | dictionary_locale?: string | undefined; 218 | dictionary_format?: string | undefined; 219 | dictionary_path?: string | undefined; 220 | } 221 | | undefined; 222 | storage?: 223 | | { 224 | managed_schema: string; 225 | } 226 | | undefined; 227 | tts_engine?: 228 | | { 229 | voices: { 230 | voice_name: string; 231 | lang?: string | undefined; 232 | gender?: string | undefined; 233 | event_types?: string[] | undefined; 234 | }[]; 235 | } 236 | | undefined; 237 | update_url?: string | undefined; 238 | version_name?: string | undefined; 239 | web_accessible_resources?: 240 | | (WebAccessibleResourceById | WebAccessibleResourceByMatch)[] 241 | | undefined; 242 | } 243 | 244 | interface DeclarativeNetRequestResource { 245 | id: string; 246 | enabled: boolean; 247 | path: string; 248 | } 249 | 250 | interface WebAccessibleResourceByMatch { 251 | matches: string[]; 252 | resources: string[]; 253 | use_dynamic_url?: boolean; 254 | } 255 | 256 | interface WebAccessibleResourceById { 257 | extension_ids: string[]; 258 | resources: string[]; 259 | use_dynamic_url?: boolean; 260 | } 261 | -------------------------------------------------------------------------------- /src/manifest/v3.mts: -------------------------------------------------------------------------------- 1 | import { createRequire } from "node:module"; 2 | import { ManifestTypeV3 } from './v3-type.mjs'; 3 | 4 | const require = createRequire(import.meta.url); 5 | const pkg = require("../../package.json"); 6 | 7 | const manifest: ManifestTypeV3 = { 8 | manifest_version: 3, 9 | name: pkg.displayName, 10 | version: pkg.version, 11 | description: pkg.description, 12 | permissions: [ 13 | "activeTab", 14 | "tabs", 15 | "storage", 16 | "background", 17 | "clipboardRead", 18 | "clipboardWrite", 19 | "contextMenus", 20 | "scripting", 21 | "activeTab" 22 | ], 23 | host_permissions:[""], 24 | commands: { 25 | "toggle-insight": { 26 | "suggested_key": { 27 | "default": "Ctrl+B", 28 | "windows": "Ctrl+B", 29 | "mac": "Command+B", 30 | }, 31 | "description": "打开面板" 32 | } 33 | }, 34 | icons: { 35 | "128": "public/icon-128.png", 36 | }, 37 | web_accessible_resources: [ 38 | { 39 | resources: ["public/*", "assets/*"], 40 | matches: [""], 41 | }, 42 | ], 43 | }; 44 | 45 | function getManifestV3(pageDirMap: { [x: string]: any }): ManifestTypeV3 { 46 | const pages = Object.keys(pageDirMap); 47 | 48 | if (pages.length === 0) { 49 | return manifest; 50 | } 51 | 52 | // 处理// 53 | const fixedPath = (p: string) => p.replace('\\', '/') 54 | 55 | if (pages.indexOf("options") > -1) { 56 | manifest.options_ui = { 57 | page: fixedPath(pageDirMap["options"]), 58 | open_in_tab:true 59 | }; 60 | } 61 | 62 | if (pages.indexOf("background") > -1) { 63 | manifest.background = { 64 | service_worker: fixedPath(pageDirMap["background"]), 65 | type: "module", 66 | }; 67 | } 68 | 69 | if (pages.indexOf("popup") > -1) { 70 | manifest.action = { 71 | // chrome.action.onClicked 生效的话,default_popup需要去掉 72 | default_title: "Hello World!", 73 | // default_popup: fixedPath(pageDirMap["popup"]), 74 | default_icon: "public/icon-34.png", 75 | }; 76 | } 77 | 78 | if (pages.indexOf("newtab") > -1) { 79 | manifest.chrome_url_overrides = { 80 | newtab: fixedPath(pageDirMap["newtab"]), 81 | }; 82 | } 83 | 84 | if (pages.indexOf("bookmarks") > -1) { 85 | manifest.chrome_url_overrides = { 86 | bookmarks: fixedPath(pageDirMap["bookmarks"]), 87 | }; 88 | } 89 | 90 | if (pages.indexOf("history") > -1) { 91 | manifest.chrome_url_overrides = { 92 | history: fixedPath(pageDirMap["history"]), 93 | }; 94 | } 95 | 96 | if (pages.indexOf("content") > -1) { 97 | manifest.content_scripts = [ 98 | { 99 | matches: ["http://*/*", "https://*/*",""], 100 | js: [fixedPath(pageDirMap["content"])], 101 | css: pageDirMap["content-css"], 102 | run_at: "document_start", 103 | }, 104 | ]; 105 | } 106 | 107 | if (pages.indexOf("devtools") > -1) { 108 | manifest.devtools_page = fixedPath(pageDirMap["devtools"]); 109 | } 110 | 111 | return manifest; 112 | } 113 | 114 | export default getManifestV3; 115 | -------------------------------------------------------------------------------- /src/pages/content/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import Main from "@src/components/ChatbotMain"; 3 | import { getConfigFromUrl, getConfig, addCss } from "@components/Utils" 4 | 5 | // import i18n from 'i18next'; 6 | import '@src/locales/i18nConfig' 7 | 8 | 9 | let json: any = getConfig() || {}; 10 | let id = (json.app + "_dom").replace(/\s/ig,'').trim(); 11 | const rootContainer = document.createElement('div'); 12 | rootContainer.id = id; 13 | 14 | 15 | const chatbotCallbacks = (event: any) => { 16 | const { cmd, data } = event; 17 | // console.log(event) 18 | if (cmd == 'open-chatbot-panel') { 19 | init() 20 | } else if (cmd == "close-insight") { 21 | 22 | } 23 | } 24 | 25 | async function init() { 26 | // from Bing,ChatGPT 初始化对话框 采用哪个引擎 27 | const { reader, fullscreen, userInput, from, agents } = getConfigFromUrl(); 28 | 29 | let dom = document.body; 30 | 31 | // 只运行一次 32 | let h = dom.querySelector('#' + id); 33 | if (h && h.children.length > 0) return 34 | 35 | const root = createRoot(rootContainer); 36 | root.render( 37 |
chatbotCallbacks(e)} 62 | /> 63 | ); 64 | 65 | dom.appendChild(rootContainer); 66 | addCss(); 67 | 68 | } 69 | 70 | 71 | // 如果是 72 | function readerHack(reader: boolean) { 73 | if (window.location.hostname.match('producthunt.com')) return false 74 | return reader 75 | } 76 | 77 | // document.addEventListener('readystatechange', (e) => { 78 | // if (document.readyState == 'interactive') { 79 | // console.log(document.readyState) 80 | // init() 81 | // } 82 | // }); 83 | window.onfocus = function (e) { 84 | console.log('window.onfocus') 85 | if (document.readyState == 'complete') { 86 | console.log('激活状态!') 87 | // bing 搜索有bug,会被直接清掉 88 | init() 89 | } 90 | } 91 | 92 | // 部分网站body下面的元素是动态植入的,需要在dom加载完后再注入,不然会被清除掉 93 | document.addEventListener("DOMContentLoaded", () => { 94 | console.log("DOMContentLoaded") 95 | init() 96 | }); 97 | -------------------------------------------------------------------------------- /src/pages/popup/Popup.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import logo from "@assets/img/logo.svg"; 4 | 5 | // let bg = chrome.extension.getBackgroundPage(); 6 | // console.log(bg) 7 | 8 | 9 | class Popup extends React.Component<{}, { 10 | extension: any 11 | }> { 12 | 13 | constructor(props: any) { 14 | super(props) 15 | this.state={ 16 | extension:chrome.extension 17 | } 18 | this.init(); 19 | } 20 | 21 | init(){ 22 | console.log(chrome.extension) 23 | } 24 | 25 | render() { 26 | return
27 |
28 | logo 29 |

30 | Edit src/pages/popup/Popup.jsx and save to reload. 31 |

32 | 38 | Learn React! 39 | 40 | 52 |
53 |
54 | } 55 | } 56 | 57 | export default Popup; 58 | 59 | // export default function Popup(): JSX.Element { 60 | // return ( 61 | //
62 | //
63 | // logo 64 | //

65 | // Edit src/pages/popup/Popup.jsx and save to reload. 66 | //

67 | // 73 | // Learn React! 74 | // 75 | // 85 | //
86 | //
87 | // ); 88 | // } 89 | -------------------------------------------------------------------------------- /src/pages/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Popup 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/pages/popup/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | import Popup from "@pages/popup/Popup"; 5 | 6 | function init() { 7 | // const rootContainer = document.querySelector("#__root"); 8 | // if (!rootContainer) throw new Error("Can't find Popup root element"); 9 | // const root = createRoot(rootContainer); 10 | // root.render(); 11 | } 12 | 13 | init(); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "types": ["node"], 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": ".", 19 | "paths": { 20 | "@src/*": ["src/*"], 21 | "@assets/*": ["src/assets/*"], 22 | "@pages/*": ["src/pages/*"], 23 | "@lib/*": ["src/lib/*"], 24 | "@components/*":["src/components/*"] 25 | } 26 | }, 27 | "include": ["src", "scripts"], 28 | } 29 | --------------------------------------------------------------------------------