From f35ba8e5a7d8b7f5e07f044c2b138627bfc37293 Mon Sep 17 00:00:00 2001 From: shilin <39396378+shilin66@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:53:01 +0800 Subject: [PATCH] feat(chatbot-extension): a Chrome extension that can be using for chat with AI on any website (#2235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(chatbot-extension): a Chrome extension that can be using for chat with AI on any website * fix: 插件支持语音输入 feat:chatbot支持切换 * fix: 切换chatbot后,自动隐藏bot列表 --- .../fastgpt_agent/img/favicon16.png | Bin 0 -> 1212 bytes .../fastgpt_agent/img/favicon32.png | Bin 0 -> 1012 bytes .../fastgpt_agent/img/fullScreen.png | Bin 0 -> 1986 bytes .../fastgpt_agent/img/setting.png | Bin 0 -> 4052 bytes .../fastgpt_agent/manifest.json | 42 ++ .../fastgpt_agent/src/background.js | 51 ++ .../fastgpt_agent/src/content.js | 533 ++++++++++++++++++ .../fastgpt_agent/src/popup.html | 108 ++++ .../fastgpt_agent/src/popup.js | 66 +++ .../fastgpt_agent/src/setting.html | 382 +++++++++++++ .../fastgpt_agent/src/setting.js | 283 ++++++++++ 11 files changed, 1465 insertions(+) create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/img/favicon16.png create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/img/favicon32.png create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/img/fullScreen.png create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/img/setting.png create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/manifest.json create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/background.js create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/content.js create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/popup.html create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/popup.js create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/setting.html create mode 100644 projects/app/public/chrome_extension/fastgpt_agent/src/setting.js diff --git a/projects/app/public/chrome_extension/fastgpt_agent/img/favicon16.png b/projects/app/public/chrome_extension/fastgpt_agent/img/favicon16.png new file mode 100644 index 0000000000000000000000000000000000000000..db96f84ad6c8712d569d0c7e2e6473081ac2314f GIT binary patch literal 1212 zcmaKqdrVt(6vuy;+EP~@9pE0ww1#Xp5b9llt#puB){d6ZZYzbcD5TKGZ04ZA_sT}l2+9zFNS4mHh{*BWRvZ08Z$9_;``vTz z`JM0i{%*@oT~_#t*cF73aE)52$63hUd=8G{q{v*Hp4h9vNQk_LkkxkAr@PV#p~iRS7*zOytQ<0`U<(78wUFt6tOKy^ zHOM&#ntIST!tNuGS_Xw}pml=jO)z(W?I=_ohteKs>IcRJWG!^N!EqAmPC>&NaGr(6 zx8ZO<96k@t1JL#!Y^j9J<*>=l?h^pl7)fVX@GXLitqxGsuwy^~YWOFpCqQ-sD=Y{q z41!Vv>}dw+ezqc8$X^%r2PkRxx9RUxSqJI^pgY9&6~NK~n7gNi1;4L6#x@&ZZzojr zL3tlLR{PsW2atW;Y_n)Z5cDKS@5j)433@KU(Z4N1myr6?aPTba6@rQARgjheXnmLU z4MDr)et%x9`Rh;uCxY5gFsrkfm4~WO2^Phjal<#x176-Et0wStTN%BM{Zsz`qBPfk zTYfyGq?A*W3c+Al;_UM8i%T=IN8c9YXf-R-9*qj|j^wAL#)aS_Ts+|$ua96?3G+)$ z{!qgF12G07)}8z41E~xXxLj^X;@qfEFgPkyPK^pF&G47PYuTo+Q3_SEJxh6%{=81(tG`n;d@x%Y3H%|>I%z1s^5RAcG7 zLhAXUvlob{Bp1tKK$> zH(2=c^E#&T@xxzyzkW|W`EYSj+xOsE;hLDZsMzbFF_V$)J?UxE1i5wi?$}^0#p7LV z^4RWlcIN8)cbq)YE%6MATpekqZTZ!@(xRfGhC_T#<159tMtZ+kUvkbeJw5%@4`E?Z zU3qS6-hK783ta;jj%8f_Oc5&)#eXxF(*KDnLNX+p7{B-3t#`;&2zl-W<*wE%eXI%V z8C3yeE@mupQ-uX5BBiCtNwh3Unrfgo%4wNAnNFl>IZfYQ;4=S8*k5M0SsnkKaK?S* z1}5yTGZZlT;u?{?qReW0)godXc8kbnXH10Dx~6<#{PKLUczB0*L^G`?oOpr6B#4fW z-u_k$Ct9R*rhIPZ#Jde5@7UcNo)J%+C)DdypUReyxz#HfUuJ3-mLM9HPWgVO@%29e Dygf0O literal 0 HcmV?d00001 diff --git a/projects/app/public/chrome_extension/fastgpt_agent/img/favicon32.png b/projects/app/public/chrome_extension/fastgpt_agent/img/favicon32.png new file mode 100644 index 0000000000000000000000000000000000000000..f5619a938fd407f9712c9b4d153090d99baf407d GIT binary patch literal 1012 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zUgGKN%Km~$h(kf?$CJ}XfkJU5t`Q|ciRB6fMfqu&IjIUIl?AB^nFS@u3|t_RO92dw zEVzsIlW+vt!K3Ez=0#$M)%(9)wz`)e!>Eaj? zaro+VUyqPLk>mBx??^uH?NhDIU3{cVjC0}_NA8tntht9cbWWLch)oPobIjOcz_rr% z$}uKIEgdFKbKRWa9opex5*iT~m;KS%V5NI+w#?j%7psgf$?NSYzyEOF{onVi&+qm> zvA}nEPXtQ?%ZBqkhaFfq@B}=&#{aN2I>0o-3^A3S|+<(7$8G@dg=WVvw* zMHMx8EpTmle89tvT}zth^WF7~d5nRFuBsF;?a*er8=kU@`CNRIYnsd{$D$^~)vhlX zj$K;jP`h9&$I~Z%PaQNrSn(&ZzhA~<;&iib}(#y`JZ)#MJ~hXX07Qm=}s+<3+&~R z-<;O?a&O8Vh9iH1w(@0gm-VymPIhSxXDScVd3bW&XR~|eEb_OU|t?D_KcI=$upZfLK=dhWw4tH%fEqk-X#U?w4$6}S2&LfMNA0*C+EL+K!lAE_m zb&rnjyX}W0LgS_+vt7BnLEw6W`TGF&DWt;nuv4WdRpTSJ3 zB<`)jIk^b}%N8&+I4-dCI3ROh@aPLi2Nf0eb3gM9Sz@?13*A~Y_0ak*=Xdh=GNbv5}RLrM7{A lm4U&5sSO`cH00)|WTsW(*0Aw}g%3~zgQu&X%Q~loCIAV$eyacg literal 0 HcmV?d00001 diff --git a/projects/app/public/chrome_extension/fastgpt_agent/img/fullScreen.png b/projects/app/public/chrome_extension/fastgpt_agent/img/fullScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..5fa0187d9adca72d44eba58d60279b2a48192f6d GIT binary patch literal 1986 zcmdT_`%@EV8x3@AHmQUSR0X@phFqwqAPiPlKojJWSb@kzKx#xouvok;2nZ3#2E_|V z2r`oR305K>BIeTqqNGTyRRR+I1TR>T!4gQR1T;_(@zU*o&>!0$-uKL$Ip;agGw(d} z9u!j@)J?cl8(-*n)lRR|R$ixO1T% zncfcoxBElL5od1ViF`i;yi%uz5PsR&CNwl3N$VTk8JLfR;g5%58@y{@!$pb>?n~Kx zIt>7Q+KpNc0Cuy!-F1(S{QHcCCj3M8B`h;`b}&P?tF8MWowbEEK7aClQ|I$7mS%JL z;vsj#*xab4EW=H2$dov6^DdnQ?oIdOg&aMjt3=2X?uMIs`6(tJ8u%>(FLLn94B*j* zBNTEO*(+3m=pR@BXLiO|HV$`HA>>c&MV>su(cONJ0b5b3IkeR(qtHyJlee)Mvr3<^(A%cU|>@RPZ2Cz#j%%_V-PDs>k-dQY$9aPF(wAR0Z~xNOo#!% z)T+VWMW$kDrP$QbY(`{zQv*)vIkz*+PBRNjZ@DBD)HhuKVWMheu(qNVIH8myIgVF6 z?PSa$gs<+M_d}un0R|;Scs$l1dk@2afZpo1h`#D;8t%XT&UjV$zy=h=vfoj&O7u@L z=x1pSTfw8@8kds(Ly}TC(rR8VnO2z)ekXk-A^Iy6?9QCuv%hj9A^D=La*m{2js%#! zW1gspw7QKWu^Tc7$sSHmx^xaPLGm}*^RRgY^POi@dJdalwkN&)eBTO!+3{6W`s%kI zH6=F^Uoh2^o61Vg{@%5cGU|NwRI#&ZFWK8r=-A5?%qO~glXY#N`un@A_Ug?I&n=>` zCuIk8OJ&Vttf$1QN!l-lyvSRxBszLn(eQQV6@k^~i1QcMf0^Ep=RFx)U&K6L;|qXF z4)stHi>oNQP`lX92G<-7WH?hGme^zG`gQq8$~hrF#1`nNIHv9+fG`PCKHWbwm`fe^<&lP;cHuVFW}u_GTT=*+#N#Zp3(9RPdZrlN}Do1q6C>)6a4)}?%lZ$5G8|kJMOR&+Z zD(e!8HS=fFmuszJI|tKBFRaKIe*6(I1P9^Jzg z;^ggW%ZLYvs_ovU>Rje9+D99G`<{eBm@jPQ-#r5`LX#rZV-`0;LCb2i&~OEFHM@8^ zc0mDLT$mI5vYfCSqom}9#PAFcq?coxwr+&5dNs}5S4Rn?t!q8WW%CSI(tY8p^je}w z*F+&yix{S=B)P7d!DTPy`WDZ&7UVSi?77%<9A-AE|JT}rRKG42pNyOMZ>#CjfK^`7OT_ p03*>Yt%rzxDEhz6KQnM(>&NxGdW~+CKE$U?5(I|_X#*4SKLG-iP?i7y literal 0 HcmV?d00001 diff --git a/projects/app/public/chrome_extension/fastgpt_agent/img/setting.png b/projects/app/public/chrome_extension/fastgpt_agent/img/setting.png new file mode 100644 index 0000000000000000000000000000000000000000..b91d958e8b8df43b87c8ae3d1c70b00273d87d08 GIT binary patch literal 4052 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" + ], + "background": { + "service_worker": "src/background.js" + }, + "action": { + "default_popup": "src/popup.html", + "default_icon": { + "16": "img/favicon32.png", + "48": "img/favicon32.png", + "128": "img/favicon32.png" + } + }, + "icons": { + "16": "img/favicon32.png", + "32": "img/favicon32.png", + "48": "img/favicon32.png", + "128": "img/favicon32.png" + }, + "content_scripts": [ + { + "js": [ + "src/content.js" + ], + "matches": [""] + } + ] +} \ No newline at end of file diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/background.js b/projects/app/public/chrome_extension/fastgpt_agent/src/background.js new file mode 100644 index 000000000..5f6d3ca9c --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/background.js @@ -0,0 +1,51 @@ +let requestInterceptor = null; + +chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { + if (message.action === "startRequestInterception") { + const botSrc = message.chatbotSrc; + console.log("src", botSrc); + const urlObj = new URL(botSrc); + const domain = urlObj.host; + const searchParams = urlObj.searchParams; + const frameShareId = searchParams.get('shareId') || ''; + let frameChatId = searchParams.get('chatId') || ''; + + // 移除已有的拦截器(如果存在) + if (requestInterceptor) { + chrome.webRequest.onBeforeRequest.removeListener(requestInterceptor); + } + + requestInterceptor = function (details) { + if (details.frameId !== -1 + && details.url.includes(domain) + && details.url.includes("chat/completions")) { + console.log("Intercepted request from chatbot-iframe:", details); + if (details.requestBody.raw) { + let decoder = new TextDecoder("utf-8"); + let postData = decoder.decode(new Uint8Array(details.requestBody.raw[0].bytes)); + try { + let postDataObj = JSON.parse(postData); + let shareId = postDataObj.shareId; + let chatId = postDataObj.chatId; + + if (frameChatId !== chatId && frameShareId === shareId) { + chrome.storage.local.set({["shareId"]: shareId}); + chrome.storage.local.set({["chatId"]: chatId}); + frameChatId = chatId; + console.log(`Stored shareId: ${shareId} and chatId: ${chatId} to localStorage.`); + } + } catch (error) { + console.error("Error parsing postData:", error); + } + } + } + return {}; + }; + + chrome.webRequest.onBeforeRequest.addListener( + requestInterceptor, + { urls: [""] }, + ["requestBody"] + ); + } +}); diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/content.js b/projects/app/public/chrome_extension/fastgpt_agent/src/content.js new file mode 100644 index 000000000..fbf487aaa --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/content.js @@ -0,0 +1,533 @@ +chrome.storage.local.get(["showChatBot"], function (result) { + if (result.showChatBot === undefined || result.showChatBot) { + const chatBtnId = 'fastgpt-chatbot-button'; + const chatWindowId = 'fastgpt-chatbot-window'; + const chatWindowWrapperId = 'fastgpt-chatbot-wrapper'; + const defaultOpen = false; + const canDrag = true; + const MessageIcon = + `data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTMyNzg1NjY0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDMyQzI0Ny4wNCAzMiAzMiAyMjQgMzIgNDY0QTQxMC4yNCA0MTAuMjQgMCAwIDAgMTcyLjQ4IDc2OEwxNjAgOTY1LjEyYTI1LjI4IDI1LjI4IDAgMCAwIDM5LjA0IDIyLjRsMTY4LTExMkE1MjguNjQgNTI4LjY0IDAgMCAwIDUxMiA4OTZjMjY0Ljk2IDAgNDgwLTE5MiA0ODAtNDMyUzc3Ni45NiAzMiA1MTIgMzJ6IG0yNDQuOCA0MTZsLTM2MS42IDMwMS43NmExMi40OCAxMi40OCAwIDAgMS0xOS44NC0xMi40OGw1OS4yLTIzMy45MmgtMTYwYTEyLjQ4IDEyLjQ4IDAgMCAxLTcuMzYtMjMuMzZsMzYxLjYtMzAxLjc2YTEyLjQ4IDEyLjQ4IDAgMCAxIDE5Ljg0IDEyLjQ4bC01OS4yIDIzMy45MmgxNjBhMTIuNDggMTIuNDggMCAwIDEgOCAyMi4wOHoiIGZpbGw9IiM0ZTgzZmQiIHAtaWQ9IjQxMzMiPjwvcGF0aD48L3N2Zz4=`; + const CloseIcon = + 'data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTM1NDQxNTI2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYzNjciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDEwMjRBNTEyIDUxMiAwIDEgMSA1MTIgMGE1MTIgNTEyIDAgMCAxIDAgMTAyNHpNMzA1Ljk1NjU3MSAzNzAuMzk1NDI5TDQ0Ny40ODggNTEyIDMwNS45NTY1NzEgNjUzLjYwNDU3MWE0NS41NjggNDUuNTY4IDAgMSAwIDY0LjQzODg1OCA2NC40Mzg4NThMNTEyIDU3Ni41MTJsMTQxLjYwNDU3MSAxNDEuNTMxNDI5YTQ1LjU2OCA0NS41NjggMCAwIDAgNjQuNDM4ODU4LTY0LjQzODg1OEw1NzYuNTEyIDUxMmwxNDEuNTMxNDI5LTE0MS42MDQ1NzFhNDUuNTY4IDQ1LjU2OCAwIDEgMC02NC40Mzg4NTgtNjQuNDM4ODU4TDUxMiA0NDcuNDg4IDM3MC4zOTU0MjkgMzA1Ljk1NjU3MWE0NS41NjggNDUuNTY4IDAgMCAwLTY0LjQzODg1OCA2NC40Mzg4NTh6IiBmaWxsPSIjNGU4M2ZkIiBwLWlkPSI2MzY4Ij48L3BhdGg+PC9zdmc+'; + const FullscreenIcon = + 'data:image/png;base64,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'; + const SwitchIcon = + 'data:image/png;base64,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'; + const ChatBtn = document.createElement('div'); + ChatBtn.id = chatBtnId; + ChatBtn.style.cssText = + 'position: fixed; bottom: 30px; right: 60px; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;'; + + const ChatBtnDiv = document.createElement('img'); + ChatBtnDiv.src = defaultOpen ? CloseIcon : MessageIcon; + ChatBtnDiv.setAttribute('width', '100%'); + ChatBtnDiv.setAttribute('height', '100%'); + ChatBtnDiv.draggable = false; + + const iframeWrapper = document.createElement('div'); + iframeWrapper.id = chatWindowWrapperId; + iframeWrapper.style.cssText = + 'border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 80px; right: 60px; max-width: 90vw; min-width: 10vw; max-height: 85vh; min-height: 15vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;'; + iframeWrapper.style.visibility = defaultOpen ? 'unset' : 'hidden'; + + const iframe = document.createElement('iframe'); + iframe.referrerPolicy = 'no-referrer'; + iframe.allow = 'microphone'; + iframe.title = 'FastGPT Chat Window'; + iframe.id = chatWindowId; + iframe.style.cssText = 'border: none; width: 100%; height: 100%;'; + + iframeWrapper.appendChild(iframe); + + const fullscreenBtn = document.createElement('img'); + fullscreenBtn.src = FullscreenIcon; + fullscreenBtn.style.position = 'absolute'; + fullscreenBtn.style.background = 'none'; + fullscreenBtn.style.border = 'none'; + fullscreenBtn.style.cursor = 'pointer'; + fullscreenBtn.id = 'fullscreenBtn'; + fullscreenBtn.style.width = '35px'; + + fullscreenBtn.addEventListener('click', function () { + const botSrc = iframe.src; + if (botSrc) { + window.open(botSrc, '_blank'); + } + }); + + const switchBtn = document.createElement('img'); + switchBtn.src = SwitchIcon; + switchBtn.style.position = 'absolute'; + switchBtn.style.background = 'none'; + switchBtn.style.border = 'none'; + switchBtn.style.cursor = 'pointer'; + switchBtn.id = 'switchBtn'; + switchBtn.style.width = '35px'; + + switchBtn.addEventListener('click', function () { + chrome.storage.local.get(["configs", "chatbotSrc",], function (result) { + const configs = result.configs || []; + // 创建或更新列表容器 + let listWrapper = document.getElementById('configList'); + if (listWrapper) { + iframeWrapper.removeChild(listWrapper); + return; + } + listWrapper = document.createElement('div'); + listWrapper.id = 'configList'; + listWrapper.className = 'ant-dropdown-menu'; + listWrapper.style.position = 'absolute'; + listWrapper.style.zIndex = '2147483647'; + listWrapper.style.backgroundColor = '#fff'; + listWrapper.style.border = '1px solid #ccc'; + listWrapper.style.borderRadius = '4px'; + listWrapper.classList.add('ant-dropdown', 'ant-dropdown-placement-bottomRight'); + const switchBtnRect = switchBtn.getBoundingClientRect(); + const iframeWrapperRect = iframeWrapper.getBoundingClientRect(); + const switchBtnOffsetRight = iframeWrapperRect.right - switchBtnRect.right; + const switchBtnOffsetTop = switchBtnRect.top - iframeWrapperRect.top; + // 确保listWrapper存在并调整其位置 + listWrapper.style.right = switchBtnOffsetRight + 'px'; + listWrapper.style.top = (switchBtnOffsetTop + switchBtn.offsetHeight) + 'px'; + listWrapper.style.padding = '5px'; + // 显示所有chatbot名称 + configs.forEach((config) => { + const item = document.createElement('div'); + item.textContent = config.name; + item.className = 'ant-dropdown-menu-item'; // 使用 Ant Design 的类名 + item.style.cursor = 'pointer'; + item.style.padding = '5px 16px'; + item.style.borderRadius = '4px'; + // 设置默认样式 + item.style.position = 'relative'; + item.style.lineHeight = '22px'; + item.style.color = '#606266'; + item.style.fontSize = '14px'; + item.style.whiteSpace = 'nowrap'; + item.style.textAlign = 'left'; + item.style.boxSizing = 'border-box'; + item.style.background = '#fff'; + item.style.borderBottomColor = '#e8eaec'; + item.style.borderBottomStyle = 'solid'; + item.style.borderBottomWidth = '1px'; + + // 设置选中样式 + if (config.url === result.chatbotSrc) { + item.style.color = '#1890ff'; + item.style.fontWeight = 'bold'; + item.style.background = '#e6f7ff'; + } + + // 为每个列表项添加点击事件监听器 + item.addEventListener('click', function () { + // 更新样式,移除其他项的蓝色 + const items = listWrapper.querySelectorAll('.ant-dropdown-menu-item'); + items.forEach((i) => { + i.style.color = '#606266'; + i.style.fontWeight = 'normal'; + i.style.background = '#fff'; + }); + // 设置当前项为蓝色 + item.style.color = '#1890ff'; + item.style.fontWeight = 'bold'; + item.style.background = '#e6f7ff'; + + // 更新chatbotSrc的值 + chrome.storage.local.set({chatbotSrc: config.url}, function () { + console.log('Updated chatbotSrc:', config.url); + }); + + // 更新iframe的src + loadChatBotIframe(document.getElementById(chatWindowWrapperId)); + }); + + listWrapper.appendChild(item); + }); + + // 将列表容器添加到body中或确保它已经存在 + if (!iframeWrapper.contains(listWrapper)) { + iframeWrapper.appendChild(listWrapper); + } + }); + }); + + document.body.appendChild(iframeWrapper); + + let chatBtnDragged = false; + let chatBtnDown = false; + let chatBtnMouseX; + let chatBtnMouseY; + + ChatBtn.addEventListener('click', function () { + if (chatBtnDragged) { + chatBtnDragged = false; + return; + } + const chatWindow = document.getElementById(chatWindowWrapperId); + if (!chatWindow) return; + + const visibilityVal = chatWindow.style.visibility; + if (visibilityVal === 'hidden') { + ChatBtnDiv.src = CloseIcon; + loadChatBotIframe(chatWindow); + } else { + chatWindow.style.visibility = 'hidden'; + const tmpBtn = document.getElementById('fullscreenBtn'); + const tmpBtn1 = document.getElementById('switchBtn'); + const tmpEl = document.getElementById('configList'); + if (tmpBtn) { + chatWindow.removeChild(tmpBtn); + } + if (tmpBtn1) { + chatWindow.removeChild(tmpBtn1); + } + if (tmpEl) { + chatWindow.removeChild(tmpEl); + } + ChatBtnDiv.src = MessageIcon; + } + }); + + function loadChatBotIframe(chatWindow) { + chrome.storage.local.get(["chatbotSrc", "shareId", "chatId", "fastUID", "chatBotWidth", "chatBotHeight"], function (result) { + let botSrc = result.chatbotSrc; + if (!botSrc || botSrc === 'about:blank' || botSrc === '') { + console.log("Can't find botSrc"); + iframe.src = 'data:text/html;charset=utf-8,没有配置机器人地址'; + chatWindow.style.visibility = 'unset'; + return; + } + let fastUID = result.fastUID; + if (!fastUID || fastUID === '') { + fastUID = generateUUID(); + chrome.storage.local.set({ + fastUID: fastUID + }); + } + chrome.runtime.sendMessage({ + action: "startRequestInterception", + chatbotSrc: botSrc + }); + console.log('fastUID:', fastUID); + botSrc = botSrc + "&authToken=" + fastUID; + if (botSrc.includes(result.shareId)) { + botSrc = botSrc + "&chatId=" + result.chatId; + } + if (result.chatBotWidth && result.chatBotHeight) { + chatWindow.style.width = result.chatBotWidth + 'px'; + chatWindow.style.height = result.chatBotHeight + 'px'; + } else { + chatWindow.style.width = '400px'; + chatWindow.style.height = '700px'; + } + iframe.src = 'about:blank'; + iframe.onload = function () { + chatWindow.style.visibility = 'unset'; + } + adjustIframePosition(chatWindow); + enableResize(iframeWrapper); + setTimeout(() => { + iframe.src = botSrc; + iframe.onload = function () { + if (parseInt(chatWindow.style.width, 10) >= 900) { + fullscreenBtn.style.top = '13px'; + fullscreenBtn.style.right = '60px'; + switchBtn.style.top = '13px'; + switchBtn.style.right = '90px'; + } else { + fullscreenBtn.style.top = '6px'; + fullscreenBtn.style.right = '50px'; + switchBtn.style.top = '6px'; + switchBtn.style.right = '80px'; + } + chatWindow.appendChild(fullscreenBtn); + chatWindow.appendChild(switchBtn); + const tmpEl = document.getElementById('configList'); + if (tmpEl) { + chatWindow.removeChild(tmpEl); + } + }; + }, 100); + }); + } + + ChatBtn.addEventListener('mousedown', (e) => { + e.stopPropagation(); + + if (!chatBtnMouseX && !chatBtnMouseY) { + chatBtnMouseX = e.clientX; + chatBtnMouseY = e.clientY; + } + + chatBtnDown = true; + }); + + window.addEventListener('mousemove', throttle(handleMouseMove, 16)); // 60fps + window.addEventListener('mouseup', handleMouseUp); + + function handleMouseMove(e) { + e.stopPropagation(); + if (!canDrag || !chatBtnDown) return; + + chatBtnDragged = true; + const transformX = e.clientX - chatBtnMouseX; + const transformY = e.clientY - chatBtnMouseY; + + ChatBtn.style.transform = `translate3d(${transformX}px, ${transformY}px, 0)`; + + adjustIframePosition(document.getElementById(chatWindowWrapperId)); + } + + function handleMouseUp(e) { + chatBtnDown = false; + adjustIframePosition(document.getElementById(chatWindowWrapperId)); + + window.removeEventListener('mousemove', handleMouseMove); + } + + ChatBtn.appendChild(ChatBtnDiv); + document.body.appendChild(ChatBtn); + + function generateUUID() { + const randomString = 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + const randomHex = (Math.random() * 16) | 0; + return randomHex.toString(16); + }); + + const timestamp = Date.now().toString(16); + + const extraRandom = (Math.random() * 1e16).toString(16); + + return `${randomString}-${timestamp}-${extraRandom}`; + } + + function adjustIframePosition(chatWindow) { + const chatBtnRect = ChatBtn.getBoundingClientRect(); + const chatBtnWidth = chatBtnRect.width; + const chatBtnHeight = chatBtnRect.height; + const chatBtnLeft = chatBtnRect.left; + const chatBtnTop = chatBtnRect.top; + + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight; + + const iframeWidth = parseInt(chatWindow.style.width, 10); + const iframeHeight = parseInt(chatWindow.style.height, 10); + + const iframeTopLeft = {x: chatBtnLeft - iframeWidth, y: chatBtnTop - iframeHeight}; + const iframeTopRight = {x: chatBtnLeft + chatBtnWidth, y: chatBtnTop - iframeHeight}; + const iframeBottomLeft = {x: chatBtnLeft - iframeWidth, y: chatBtnTop + chatBtnHeight}; + const iframeBottomRight = {x: chatBtnLeft + chatBtnWidth, y: chatBtnTop + chatBtnHeight}; + + let bestPosition = iframeTopLeft; + let bestDistance = Infinity; + + const positions = [iframeTopLeft, iframeTopRight, iframeBottomLeft, iframeBottomRight]; + positions.forEach(position => { + const distance = Math.sqrt(Math.pow(position.x, 2) + Math.pow(position.y, 2)); + + if (position.x + iframeWidth > screenWidth) { + position.x = screenWidth - iframeWidth; + } + if (position.x < 0) { + position.x = 0; + } + if (position.y + iframeHeight > screenHeight) { + position.y = screenHeight - iframeHeight; + } + if (position.y < 0) { + position.y = 0; + } + + if (distance < bestDistance) { + bestPosition = position; + bestDistance = distance; + } + }); + + chatWindow.style.left = `${bestPosition.x}px`; + chatWindow.style.top = `${bestPosition.y}px`; + } + + function throttle(func, limit) { + let lastFunc; + let lastRan; + return function () { + const context = this; + const args = arguments; + if (!lastRan) { + func.apply(context, args); + lastRan = Date.now(); + } else { + clearTimeout(lastFunc); + lastFunc = setTimeout(function () { + if ((Date.now() - lastRan) >= limit) { + func.apply(context, args); + lastRan = Date.now(); + } + }, limit - (Date.now() - lastRan)); + } + } + } + + function enableResize(iframeWrapper) { + let isResizing = false; + let lastDownX = 0; + let lastDownY = 0; + let resizeDirection = ''; + + // 创建八个调整大小的句柄 + const handles = ['nw-resize', 'ne-resize', 'sw-resize', 'se-resize', 'n-resize', 's-resize', 'w-resize', 'e-resize']; + const directions = ['tl', 'tr', 'bl', 'br', 't', 'b', 'l', 'r']; + + handles.forEach((cursorType, index) => { + const handle = createResizeHandle(cursorType); + iframeWrapper.appendChild(handle); + positionHandle(handle, directions[index]); + handle.addEventListener('mousedown', (e) => startResizing(e, directions[index])); + }); + + function createResizeHandle(cursorType) { + const handle = document.createElement('div'); + handle.style.width = '15px'; + handle.style.height = '15px'; + handle.style.background = 'transparent'; + handle.style.position = 'absolute'; + handle.style.cursor = cursorType; + return handle; + } + + function positionHandle(handle, direction) { + switch (direction) { + case 'tl': + handle.style.top = '0'; + handle.style.left = '0'; + break; + case 'tr': + handle.style.top = '0'; + handle.style.right = '0'; + break; + case 'bl': + handle.style.bottom = '0'; + handle.style.left = '0'; + break; + case 'br': + handle.style.bottom = '0'; + handle.style.right = '0'; + break; + case 't': + handle.style.top = '0'; + handle.style.left = '50%'; + handle.style.transform = 'translateX(-50%)'; + handle.style.width = `${iframeWrapper.offsetWidth - 30}px`; + handle.style.height = '3px'; + break; + case 'b': + handle.style.bottom = '0'; + handle.style.left = '50%'; + handle.style.transform = 'translateX(-50%)'; + handle.style.width = `${iframeWrapper.offsetWidth - 30}px`; + handle.style.height = '3px'; + break; + case 'l': + handle.style.top = '50%'; + handle.style.left = '0'; + handle.style.transform = 'translateY(-50%)'; + handle.style.width = '3px'; + handle.style.height = `${iframeWrapper.offsetHeight - 30}px`; + break; + case 'r': + handle.style.top = '50%'; + handle.style.right = '0'; + handle.style.transform = 'translateY(-50%)'; + handle.style.width = '3px'; + handle.style.height = `${iframeWrapper.offsetHeight - 30}px`; + break; + } + } + + function startResizing(e, direction) { + isResizing = true; + lastDownX = e.clientX; + lastDownY = e.clientY; + resizeDirection = direction; + iframeWrapper.style.pointerEvents = 'none'; // 禁用 iframe 的鼠标事件 + e.preventDefault(); + } + + document.addEventListener('mousemove', throttle(handleResizeMouseMove, 50)); + document.addEventListener('mouseup', handleResizeMouseUp); + + function handleResizeMouseMove(e) { + if (!isResizing) return; + + const offsetX = e.clientX - lastDownX; + const offsetY = e.clientY - lastDownY; + requestAnimationFrame(() => { + switch (resizeDirection) { + case 'tl': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth - offsetX}px`; + iframeWrapper.style.height = `${iframeWrapper.offsetHeight - offsetY}px`; + iframeWrapper.style.left = `${iframeWrapper.offsetLeft + offsetX}px`; + iframeWrapper.style.top = `${iframeWrapper.offsetTop + offsetY}px`; + break; + case 'tr': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth + offsetX}px`; + iframeWrapper.style.height = `${iframeWrapper.offsetHeight - offsetY}px`; + iframeWrapper.style.top = `${iframeWrapper.offsetTop + offsetY}px`; + break; + case 'bl': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth - offsetX}px`; + iframeWrapper.style.height = `${iframeWrapper.offsetHeight + offsetY}px`; + iframeWrapper.style.left = `${iframeWrapper.offsetLeft + offsetX}px`; + break; + case 'br': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth + offsetX}px`; + iframeWrapper.style.height = `${iframeWrapper.offsetHeight + offsetY}px`; + break; + case 't': + iframeWrapper.style.height = `${iframeWrapper.offsetHeight - offsetY}px`; + iframeWrapper.style.top = `${iframeWrapper.offsetTop + offsetY}px`; + break; + case 'b': + iframeWrapper.style.height = `${iframeWrapper.offsetHeight + offsetY}px`; + break; + case 'l': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth - offsetX}px`; + iframeWrapper.style.left = `${iframeWrapper.offsetLeft + offsetX}px`; + break; + case 'r': + iframeWrapper.style.width = `${iframeWrapper.offsetWidth + offsetX}px`; + break; + } + lastDownX = e.clientX; + lastDownY = e.clientY; + if (parseInt(iframeWrapper.style.width, 10) >= 900) { + fullscreenBtn.style.top = '13px'; + fullscreenBtn.style.right = '60px'; + switchBtn.style.top = '13px'; + switchBtn.style.right = '90px'; + } else { + fullscreenBtn.style.top = '6px'; + fullscreenBtn.style.right = '50px'; + switchBtn.style.top = '6px'; + switchBtn.style.right = '80px'; + } + }); + } + + function handleResizeMouseUp() { + console.log('handleResizeMouseUp'); + if (isResizing) { + isResizing = false; // 将 isResizing 重置为 false + enableResize(iframeWrapper); + iframeWrapper.style.pointerEvents = 'auto'; // 恢复 iframe 的鼠标事件 + chrome.storage.local.set({ + chatBotWidth: iframeWrapper.offsetWidth, + chatBotHeight: iframeWrapper.offsetHeight + }); + } + } + } + } +}); diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/popup.html b/projects/app/public/chrome_extension/fastgpt_agent/src/popup.html new file mode 100644 index 000000000..f69f9bfe5 --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/popup.html @@ -0,0 +1,108 @@ + + + + + Chatbot Extension + + + + + +
+ + + + + +
+ + + + + diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/popup.js b/projects/app/public/chrome_extension/fastgpt_agent/src/popup.js new file mode 100644 index 000000000..32e11baf4 --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/popup.js @@ -0,0 +1,66 @@ +document.addEventListener('DOMContentLoaded', function () { + const chatbotIframe = document.getElementById('chatbot-iframe'); + const fullScreenBtn = document.getElementById('fullScreen'); + const configBtn = document.getElementById('config-btn'); + const overlay = document.getElementById('configOverlay'); + configBtn.addEventListener('click', function () { + window.location.href = 'setting.html' + }); + + // 监听 chatbotIframe 加载完成事件 + chatbotIframe.addEventListener('load', function() { + // 当 iframe 加载完成后显示按钮 + fullScreenBtn.style.display = 'inline-block'; + configBtn.style.display = 'inline-block'; + }); + chrome.storage.local.get(["chatbotSrc", "shareId", "chatId", "fastUID"]).then((result) => { + const botSrc = result.chatbotSrc; + let fastUID = result.fastUID; + if (!fastUID || fastUID === '') { + fastUID = generateUUID(); + chrome.storage.local.set({ + fastUID: fastUID + }); + } + console.log('fastUID is', fastUID); + console.log("chatbotSrc is " + botSrc); + console.log("shareId is " + result.shareId); + console.log("chatId is " + result.chatId); + chatbotIframe.src = result.chatbotSrc + "&authToken=" + fastUID; + if (!botSrc || botSrc === 'about:blank' || botSrc === '') { + overlay.style.display = 'flex'; + } else { + if (botSrc.includes(result.shareId)) { + chatbotIframe.src = chatbotIframe.src + "&chatId=" + result.chatId; + console.log('chatbotIframe.src', chatbotIframe.src); + } + overlay.style.display = 'none'; + chrome.runtime.sendMessage({ + action: "startRequestInterception", + chatbotSrc: botSrc + }); + } + }); + + fullScreenBtn.addEventListener('click', function () { + const iframe = document.getElementById('chatbot-iframe'); + if (iframe) { + const iframeSrc = iframe.src; + console.log('fullScreenSrc', iframeSrc) + chrome.tabs.create({url: iframeSrc}); + } + }); +}); + +function generateUUID() { + const randomString = 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + const randomHex = (Math.random() * 16) | 0; + return randomHex.toString(16); + }); + + const timestamp = Date.now().toString(16); + + const extraRandom = (Math.random() * 1e16).toString(16); + + return `${randomString}-${timestamp}-${extraRandom}`; +} diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/setting.html b/projects/app/public/chrome_extension/fastgpt_agent/src/setting.html new file mode 100644 index 000000000..d906659e2 --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/setting.html @@ -0,0 +1,382 @@ + + + + + + 配置项管理器 + + + + + +
+

ChatBot配置

+

页面机器人:

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +

机器人地址:

+ + + + + + + + + + +
名称地址操作选择BOT
+ + + \ No newline at end of file diff --git a/projects/app/public/chrome_extension/fastgpt_agent/src/setting.js b/projects/app/public/chrome_extension/fastgpt_agent/src/setting.js new file mode 100644 index 000000000..d2f68d537 --- /dev/null +++ b/projects/app/public/chrome_extension/fastgpt_agent/src/setting.js @@ -0,0 +1,283 @@ +let chatbotSrc = ''; +let configs = []; + +document.addEventListener('DOMContentLoaded', async function () { + const storageData = await chrome.storage.local.get(["chatbotSrc", "configs", "showChatBot", "chatBotWidth", "chatBotHeight"]); + chatbotSrc = storageData.chatbotSrc || ''; + configs = storageData.configs || []; + const showChatBot = storageData.showChatBot === undefined ? true : storageData.showChatBot; + const chatBotWidth = storageData.chatBotWidth || 400; + const chatBotHeight = storageData.chatBotHeight || 700; + + await loadConfigs(configs, chatbotSrc); + + document.getElementById('addConfigButton').addEventListener('click', handleAddButtonClick); + document.getElementById('startChatButton').addEventListener('click', () => window.location.href = 'popup.html'); + + // 监听开关和输入框变化事件 + const showChatBotSwitch = document.getElementById('showChatBotSwitch'); + const chatBotWidthInput = document.getElementById('chatBotWidthInput'); + const chatBotHeightInput = document.getElementById('chatBotHeightInput'); + + showChatBotSwitch.addEventListener('change', () => handleShowChatBotChange(showChatBotSwitch.checked)); + chatBotWidthInput.addEventListener('change', () => handleChatBotWidthChange(chatBotWidthInput.value)); + chatBotHeightInput.addEventListener('change', () => handleChatBotHeightChange(chatBotHeightInput.value)); + + // 初始化开关和输入框的值 + showChatBotSwitch.checked = showChatBot; + chatBotWidthInput.value = chatBotWidth; + chatBotHeightInput.value = chatBotHeight; + +}); + +async function loadConfigs(configs, chatbotSrc) { + const configList = document.getElementById('configList'); + configList.innerHTML = ''; + configs.forEach(config => { + const row = createConfigRow(config, chatbotSrc); + configList.appendChild(row); + }); +} + +function createConfigRow(config, chatbotSrc) { + const row = document.createElement('tr'); + row.innerHTML = ` + ${config.name} + ${config.url} + + + | + + + + + + `; + row.querySelector('.editButton').addEventListener('click', () => handleEditButtonClick(row, config)); + row.querySelector('.deleteButton').addEventListener('click', () => handleDeleteButtonClick(row, config)); + row.querySelector('.selectBot').addEventListener('change', () => handleSelectBotChange(config.url)); + return row; +} + + +async function handleDeleteButtonClick(row, config) { + const configList = document.getElementById('configList'); + const index = Array.from(configList.children).indexOf(row); + const selectedUrl = config.url; + if (selectedUrl === chatbotSrc) { + chatbotSrc = 'about:blank'; + await updateStorage('chatbotSrc', chatbotSrc); + await updateStorage('chatId', ''); + await updateStorage('shareId', ''); + } + configs.splice(index, 1); + await updateStorage('configs', configs); + row.remove(); +} + +async function handleSelectBotChange(url) { + chatbotSrc = url; + await updateStorage('chatbotSrc', chatbotSrc); + updateSelectedRadioButton(); +} + +function updateSelectedRadioButton() { + document.querySelectorAll('.selectBot').forEach(radio => { + if (radio.closest('tr').querySelector('td:nth-child(2)').textContent === chatbotSrc) { + radio.checked = true; + } else { + radio.checked = false; + } + }); +} + + +function showError(message) { + const errorMsg = document.getElementById('errorMsg'); + errorMsg.textContent = message; + errorMsg.style.opacity = 1; // 显示错误消息 + + // 设置一个定时器,在5秒后隐藏错误消息 + setTimeout(() => { + errorMsg.style.opacity = 0; // 隐藏错误消息 + }, 3000); +} + + +async function updateStorage(key, value) { + try { + await chrome.storage.local.set({[key]: value}); + console.log(`${key} 已更新: ${value}`); + } catch (error) { + console.error(`更新 ${key} 出错:`, error); + } +} + +function showEditControls(row, isNewConfig = false, config = null) { + const nameCell = row.querySelector('td:nth-child(1)'); + const urlCell = row.querySelector('td:nth-child(2)'); + const name = isNewConfig ? '' : config.name; + const url = isNewConfig ? '' : config.url; + + nameCell.innerHTML = ``; + urlCell.innerHTML = ``; + + const iconContainer = document.createElement('div'); + iconContainer.style.display = 'flex'; + iconContainer.style.flexDirection = 'column'; + iconContainer.style.position = 'absolute'; + iconContainer.style.top = '15px'; + iconContainer.style.left = '-5px'; + + const confirmIcon = document.createElement('span'); + confirmIcon.textContent = '√'; + confirmIcon.style.color = 'green'; + confirmIcon.style.cursor = 'pointer'; + confirmIcon.style.margin = '0 0 10px 0'; + confirmIcon.classList.add('icon-hover'); + confirmIcon.classList.add('confirmButton'); + confirmIcon.addEventListener('click', () => { + handleConfirmButtonClick(row, isNewConfig); + }); + + const cancelIcon = document.createElement('span'); + cancelIcon.textContent = 'X'; + cancelIcon.style.color = 'red'; + cancelIcon.style.cursor = 'pointer'; + cancelIcon.classList.add('icon-hover'); + cancelIcon.classList.add('cancelButton') + cancelIcon.addEventListener('click', () => { + handleCancelButtonClick(row, isNewConfig); + }); + + iconContainer.appendChild(confirmIcon); + iconContainer.appendChild(cancelIcon); + + const cell = row.querySelector('td:nth-child(3)'); + cell.insertBefore(iconContainer, cell.firstChild); +} + +function handleAddButtonClick() { + const newRow = document.createElement('tr'); + newRow.innerHTML = ` + + + + + + + `; + document.getElementById('configList').appendChild(newRow); + showEditControls(newRow, true); +} + +function handleEditButtonClick(row, config) { + disableAllEditButtons(); + showEditControls(row, false, config); +} + +function isConfigUnique(name, url, index) { + return !configs.some((config, i) => + i !== index && (config.name === name || config.url === url) + ); +} + +function checkConfig(name, url, index) { + let errorMsg = ''; + if (!name || !url) { + errorMsg = '名称和地址都是必填项。'; + } else if (!isConfigUnique(name, url, index)) { + errorMsg = '名称或地址不能重复。'; + } + return errorMsg; +} + +function handleConfirmButtonClick(row, isNewConfig = false) { + const name = row.querySelector('.editName').value; + const url = row.querySelector('.editUrl').value; + const index = isNewConfig ? -1 : Array.from(document.getElementById('configList').children).indexOf(row); + const errorMsg = checkConfig(name, url, index); + if (errorMsg) { + showError(errorMsg); + return; + } + + if (isNewConfig) { + const newConfig = {name, url}; + configs.push(newConfig); + updateStorage('configs', configs); + + } else { + const config = configs[index]; + config.name = name; + if (config.url === chatbotSrc) { + chatbotSrc = url; + updateStorage('chatId', ''); + updateStorage('shareId', ''); + chrome.runtime.sendMessage({ + action: "startRequestInterception", + chatbotSrc: chatbotSrc + }); + } + config.url = url; + updateStorage('configs', configs); + updateStorage('chatbotSrc', chatbotSrc); + } + loadConfigs(configs, chatbotSrc); + row.remove(); + enableAllEditButtons(); +} + + +function handleCancelButtonClick(row, isNewConfig = false) { + if (isNewConfig) { + row.remove(); + } else { + const index = Array.from(document.getElementById('configList').children).indexOf(row); + const config = configs[index]; + row.querySelector('td:nth-child(1)').textContent = config.name; + row.querySelector('td:nth-child(2)').textContent = config.url; + row.querySelector('.editButton').disabled = false; + // 移除编辑模式下的确认和取消按钮 + const iconContainer = row.querySelector('td:nth-child(3) > div'); + if (iconContainer) { + iconContainer.remove(); + } + } + enableAllEditButtons(); +} + +async function handleShowChatBotChange(showChatBot) { + await updateStorage('showChatBot', showChatBot); +} + +async function handleChatBotWidthChange(width) { + const parsedWidth = parseInt(width); + if (!isNaN(parsedWidth)) { + await updateStorage('chatBotWidth', parsedWidth); + } +} + +async function handleChatBotHeightChange(height) { + const parsedHeight = parseInt(height); + if (!isNaN(parsedHeight)) { + await updateStorage('chatBotHeight', parsedHeight); + } +} + +function disableAllEditButtons() { + const allEditButtons = document.querySelectorAll('.editButton'); + allEditButtons.forEach(button => { + button.disabled = true; + }); +} + +function enableAllEditButtons() { + const allEditButtons = document.querySelectorAll('.editButton'); + allEditButtons.forEach(button => { + button.disabled = false; + }); +} \ No newline at end of file