From 658201a4bab4302b38d0bce441ef827af456efee Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Thu, 1 Aug 2024 17:38:20 -0700 Subject: [PATCH 01/37] initial commit for chrome extension --- extension/.eslintrc.cjs | 21 ++++++++++ extension/.gitignore | 24 +++++++++++ extension/README.md | 8 ++++ extension/index.html | 12 ++++++ extension/package.json | 31 ++++++++++++++ extension/postcss.config.js | 6 +++ extension/public/background.js | 52 +++++++++++++++++++++++ extension/public/icon128.png | Bin 0 -> 11813 bytes extension/public/icon16.png | Bin 0 -> 703 bytes extension/public/icon32.png | Bin 0 -> 1873 bytes extension/public/icon48.png | Bin 0 -> 3122 bytes extension/public/manifest.json | 25 +++++++++++ extension/src/App.jsx | 30 ++++++++++++++ extension/src/components/Config.jsx | 60 +++++++++++++++++++++++++++ extension/src/index.css | 31 ++++++++++++++ extension/src/main.jsx | 10 +++++ extension/src/media/anything-llm.png | Bin 0 -> 6324 bytes extension/tailwind.config.js | 8 ++++ extension/vite.config.js | 14 +++++++ 19 files changed, 332 insertions(+) create mode 100644 extension/.eslintrc.cjs create mode 100644 extension/.gitignore create mode 100644 extension/README.md create mode 100644 extension/index.html create mode 100644 extension/package.json create mode 100644 extension/postcss.config.js create mode 100644 extension/public/background.js create mode 100644 extension/public/icon128.png create mode 100644 extension/public/icon16.png create mode 100644 extension/public/icon32.png create mode 100644 extension/public/icon48.png create mode 100644 extension/public/manifest.json create mode 100644 extension/src/App.jsx create mode 100644 extension/src/components/Config.jsx create mode 100644 extension/src/index.css create mode 100644 extension/src/main.jsx create mode 100644 extension/src/media/anything-llm.png create mode 100644 extension/tailwind.config.js create mode 100644 extension/vite.config.js diff --git a/extension/.eslintrc.cjs b/extension/.eslintrc.cjs new file mode 100644 index 00000000000..3e212e1d430 --- /dev/null +++ b/extension/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/extension/.gitignore b/extension/.gitignore new file mode 100644 index 00000000000..a547bf36d8d --- /dev/null +++ b/extension/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/extension/README.md b/extension/README.md new file mode 100644 index 00000000000..f768e33fc94 --- /dev/null +++ b/extension/README.md @@ -0,0 +1,8 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/extension/index.html b/extension/index.html new file mode 100644 index 00000000000..fc5f222d9ff --- /dev/null +++ b/extension/index.html @@ -0,0 +1,12 @@ + + + + + + AnythingLLM Document Saver + + +
+ + + diff --git a/extension/package.json b/extension/package.json new file mode 100644 index 00000000000..5f80693b062 --- /dev/null +++ b/extension/package.json @@ -0,0 +1,31 @@ +{ + "name": "extension", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "nodemon --watch src --watch public -e js,jsx,css,html --exec \"yarn dev:build\"", + "dev:build": "vite build && cp public/background.js dist/", + "build": "vite build && cp public/background.js dist/", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.3", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "nodemon": "^3.1.4", + "postcss": "^8.4.40", + "tailwindcss": "^3.4.7", + "vite": "^5.3.4" + } +} diff --git a/extension/postcss.config.js b/extension/postcss.config.js new file mode 100644 index 00000000000..2e7af2b7f1a --- /dev/null +++ b/extension/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/extension/public/background.js b/extension/public/background.js new file mode 100644 index 00000000000..25c8de0637c --- /dev/null +++ b/extension/public/background.js @@ -0,0 +1,52 @@ +chrome.runtime.onInstalled.addListener(() => { + chrome.contextMenus.create({ + id: "saveToAnythingLLM", + title: "Save to AnythingLLM", + contexts: ["selection"], + }); +}); + +chrome.contextMenus.onClicked.addListener((info, tab) => { + if (info.menuItemId === "saveToAnythingLLM") { + const selectedText = info.selectionText; + saveToAnythingLLM(selectedText, tab.title); + } +}); + +async function saveToAnythingLLM(selectedText, pageTitle) { + try { + const { apiKey } = await chrome.storage.sync.get(["apiKey"]); + + if (!apiKey) { + console.error( + "API key not set. Please configure it in the extension options." + ); + return; + } + + const response = await fetch( + "http://localhost:3001/api/v1/document/raw-text", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + textContent: selectedText, + metadata: { + title: pageTitle, + }, + }), + } + ); + + if (response.ok) { + console.log("Text saved successfully!"); + } else { + console.error("Failed to save text."); + } + } catch (error) { + console.error("An error occurred:", error); + } +} diff --git a/extension/public/icon128.png b/extension/public/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..2433337c10e828c9828a24bd556f0d60ff930f5e GIT binary patch literal 11813 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 Y_nP!HL-a)}z zWVEJ8{jwF3cccP#MeOm5&3T=m8Z59;z1EQ}_OA;t)5@r3h$enRFn;-*?WakC3og1C zW5)64zS-u-UOZgckTZy$OH_uX@Mz}pJBiD``aI6h(cv72a+wKargtWD|IQPD!~y)9*Hv+v=`oe>BSdu$E~-x!6eD{ zO}<`|B&eC`VG|}z#JLw-fC&>O;I&s@#m|29BW&8Z39K|U8T*|*leZIH7+xyF(zn2-f5_|oulNh`5y`Ljcz6BE;+`9w^i0YA3qilPv zxtAmf#!VQH3ogDmFhQ%fZQHitTVK5ayLaz~OX@5nsCqBr@a}Fbyy6PXIOGs?4jYC; zXU)RjUVb_1ogFUOGY61g$jN3b+qMVo=aA!CNo`j(sU9;)_2N8$O?c0WV?kOWTmid6 zh#h&)8U!z?LbvD{snl@OO^ctgeNP}3EY%3K6n=i2hnJ`XU0q$cXwf3nJ33m{+sHE9 z`1P-0!-fq2hY6iQhL7@d1une!VvHI+I(W~-Nt5v2^UgDQfFzK>_Cfo;Nb@b=VxH34 z8?#tEa0zTG_60AZ|D9VW&@HKAQ?*Fs_;5b-Zs}1WIGxTGQFYrn?#y6PCgFjhSVOY{ zLFY9(@g{Ql?JS~H;oJ)^#MtreDSbcu-uJNb=@kYL<)6oM*A?OPGtb0d&6-vE?xT-8 z4zIlY5`O#8|8viC!zQUx&iZYRV8C_l7+KL~R|IWm%mA?Vx*ZjL2bQ1;96GWJEXOVR zd_3axo96e62;E*6Y@U6ZBsk@?cVYJ2quO%wF8}Q^+<(vA zE*~-Y44(1{omTf4KYl#kamKrwzWeO=o{QJkyowi}dk#t~XvMjg%jz+L_&Mkb2jJ8{ZX1IX(45)m$1bSXxT8s*Z>!w+Yp zqz~jWi#*q{dP0*ilUx%;5mA6ae9ei`5EE1ZYsx4aUa4q`X{NMP0>RO&A2UhPI9yax zK2g-p+aEh_Jl_Aoi@?;nu5H`4mH7a zFUuWIUVjkt1l;6MKPlAC3br$e$jl6CI%a}9v@j@&Y@-RsP}Yu=SR(f_?_8H*oYZKA z!qQBkNozGRFe%bn?rf>m^5a^qhFYy=uID-YNi9W^Cg|ww!UdOHg3hkamWor>$guc7 zzKIR%*9%!GsMF@|ixosP@&z5%tzC;HzxYMVI+z(#rccK?Mk(}}$EjN@*&Um{rFKhG zs5D3XG)MzuarrXL8zYbMXej2Vl0C!U1gKm0oY zFbhfh&(%tq?6?nd)9;+~&%^i$6Wfye|K|sH;+YjsJ55#2LMFK?s-4W?3=WNq8Na@7 z2_{aSjH8b`u4P@vop2)7zWOR2d*l(5GHxsksbEH8H5Vfar=IpM)H}P{3I~@{v-I0U zFp^xqzz1>a>8GQ=uOD`vARJW! zW!h&~*D#D2HL5L*|FMUEhyQ!;-EL-ZSf?#M000f(Nkl;XOSq>mEB{!anI! z->zNGp7(Kvyq(@p8+&S{N6`CPB)n3KQOpqE27>X&8$*q3HU5XzJuBG~8#eIjD{C-* z;-oMq|T<4fyWjTXEGVK8a+A zR|aplyz=tPesYM6&mnZd+r9Vpv4eRa4GeE6S^>@zC~i2b|KSO< z91HgA*2psa*G)HK!23H%94USlW>@(wZ;q2K8p?O*JJGX!L#^1 zE)4wQM|WZM(<{s%`9w}Xb5{wTPkwx2{C7GLd5xPGzj@#ROq?_-{2*7)h2cj8aaKfmAl)!W;P zpZxFdBWpCA=oKl;uI(I>cmyA*Fe3O(GIG?IYpjWs4}kTDOQnKJH%TR~0)uUvH*P@h z&Yd{)h*>UJvaiasD_7$Ci*H7w(J*~bfyo!o-!K1qi1>zp97`bzb0o;Hdc{*X?8rIj zZbK>Do6^^}8+YDz8=hPJOit_~UCoLzYdJ|PkU6ybB?kziuoeNz+?EKg1#=*5INlqN zLLk`+G;6&2@=JK_<(DyS<{{|l>I(33D7OCoemr>p68!l4-$j3azXPaSw85hSLupFTxrORgf^!4@O$;W<=;oUtLKXFo?C^Cfo(pqEft84JRTW-R$t5)W1Q!-hF z+?QKC@s?1(CCmyZ62PG8GY@0?Js{;)wvxx?pO3M&yRFvBAZxFJZE%B8@2KPOBj;ky zF~?x?v}x$<>S{XAv{`918rZmD1D<_)1y(%qI5w_d@37yu-Mf30Tz|L;@B>f_ZQ2i* zb`i^b$`}AmJ8fQ#st z#oRo%!RlgS6Dy3ndwnbN2bu$O&<1OYZdmJN$=5IRzZ+cNqO*V_`;T_XzPWy| zv&%y#kc5Fi0Bx)_&03^{3G2@VMJgIxsyPP2RL8Q0$WALl$&MHKe0GBI7gI3Rkf`-QxnVLD+5wBx zxt!e(KUSXD*<*LBx;IPvODp8Z>^Zj8RK@e?9z^5v-gG^+p}}8k4P_H{sWuw#|}QN zD8HmH5hoDv7w}DBKc40KMS^I!Sq<_T>ax;nTU|TZ}MJkuUADI*eSWlp7jH8qSHA*0x<7hmkLRjb>nZY6Z z@ni45F7S(%Y*P$;d>oJydAuDoja3F?QSZl~BWA`XCn6FBEq{`A_E!E1q6r1-F9Ls23LGp$RZ3*Pll#GOXzob|QIKV<@nFiDeo2HyuuNQv zfQa2oBZtL*AkgiD9- zCy~M>6UzJTih_fsogyuo0`)Tj>PKt^#6hC`ZzCzZqI8B=nDJ_;<%QlNCt{q#!TXz} zEDC4#+J6Vj-W4tZzBl{O3^i0)c(VB z4(lkpbPx&lqHs8TEM>jD&yFbLY@g687+xclT>JW(mzSGG&+r`v_r=qOf}j#Y;Cqw9 zG$d-Ecr@S)2TMCeYV7t>8So)sVG$wX*1_h4#8x7{2Qa~)jChq|JWGtX*#`Kg_(HY; zB$hz2ZUpkGpKYj2y6~RHgL*;p!Ud*TCIW)QlYqMIb(yddF3z20jYpxAR{$UZfZpxf z8a=~DG(dE&3#(DNE1ON2k^yfqxG)^T>-I+!dI{t+2a7F2Q+Jnww3*O`7kj7p!E=My z0x+)w0}b6!pIoH`vnO-~Wd#1j>uX>8 zV=&aVj2$~+R9dS&OhiYx1PnjDTISdn!ycO{e6wIw`_T1N897q-Q9EI zu;D#-gJ?_)csVDnZQxT_Z3y^cGY0Q2c(YJp5FDyh;pgaH`FZI7M+p2ThydUf0B5dy zkB3I>gG%9c0u6KS0l^Jq(xOaW3=ly;QYJgfCbTF1_YS~&^Xt76LfrHg z$InQL2Z~f*X$45p;6%jc-W}U7*tv82(yAs@E_cz}+l#Id!&ebw9SCO;IQ0}QWWQTG zi2Xg2C*ZHM05XTyoWf8o^Q)gbz&Dq$86!meoR{?>#W!R9c3(sUbGA)EUqOWXcJ11Q zs?w4$#!Q@;D6Os{qOSoM7EG?JVEcucF481)HJ7k`d32x%_n|D2tS_K?;Q5p1WmKPE zk7T{wZzg0{0{pXe>((`8U~LWa*s+t9O0x5TfBjDpA z5TPj&NDdODe>XJ;*X-O`zqGSs`-7V|Z*D2Q+8*x7lP6=#maX%L4g(&`B;Jka_O-sgSGnJDnPF)-0TjQJy!&JcpS)jfO)6RvafF2w)vww zw{KlLn6~ZF*p3@FF=g<(0d)fr<|6;7nwYgJbOKRJA?Q`MGmf8;*TXyu z;Wx-w_W`qjf#a%aD%vxN#He03XJzE(GBN0A>If z4I?57-rxhG$W-Pxx0E1224JJkvKJ{yzMqi(`IfEQUK;4{YYcVohrEoRFrl7lOzqvV z^RQvVhMmPkr4BF1pV zIe!?yIsQ?3e&uY}RCOzpzx?joZ*UJjbd>R3bA880&)FOzfT+Hcn}%K(HBIJ|ZUfXSrd&zpOTmhKH|imBqgwdYRz}OS^>%H? zg1Tv@1QETS`n2Y7b4?ezI8^j6d8$cuTS&|FsYgV936lCP=lCM$?2Ay~oWG-mIU6cU zN}Z;ZUMg0`D?g_st(iS$YEE@Ap!xf^BWbqo)OXr<{dU{dpE-a7tB1h3$H853&fVj1 lcOuxeY%glkulDx-;1(`#nbGfAOtAm}002ovPDHLkV1kv1QuqJ> literal 0 HcmV?d00001 diff --git a/extension/public/icon32.png b/extension/public/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..d08010ad7ea05255e3abaf6a8fff6cb68bcf84f2 GIT binary patch literal 1873 zcmV-X2d?;uP)pg<%c za*a-1d5nKfmGT87BHz$dJ2jywDjyX=N!X_x%N8eP#Za{WbL)H*Pi1a zbNv6fOyND(_}X(q2=!;38~^%f{+|}tu3bw~zdBUm zmvvpA=6w+793lcDvx4s64GcqbCg*G^rOlvMZhT_HmKhF=kDrIH(ieTGepS_VT~!s{ zdz|w)Cx9R_n+OVbAva{nL8AK2_Z#gn;+O zfiC>BjKJt&W~eHvnV2GR*f1PUG8|4mlg-Wwy}Ey~cm7o80?zxg>yG5CxFjc#%9?I7Y-x38zsSHji_ zis}D&KQtU-jZJf;sQMGZtSHqAVFlb2F=;bk3bHtsnaYZptP2#~Fq68hsaxM^qhyE+ zW=iKoyYt{2cTjNOdh0)2ea&_3*|QfH zLJ6fA3l_}h)?04k@WT%~XidtQtJh!4jvcQePKYrwdEi|R9C(*t+InS*s)1#eEjym1 z%3FK(@bE(q7bS~u!3F1YVf z+fO**#1mP(Xb~^J@(%|60asl4WsW)K81}yX4p*_jA?nQWz>`~Qhf=Ff(SWhhA|Oyt9S(G93fOZ zzTpWTdvpWNd6q9<&KEAdgeFGrz32Blx$!Cbg8{)8ZDcWGYb)p6ZFCfVGoxgI8ahfp zgqSl;jKr8`WC$W{lXA>EY93$v#yYYY&pf$_-~IYGRCN#Ue93?osELVY6oD3Fbek!m z`%#swiinU>X4phxO5N{W;+ruT4EXB0b=3VnukGB)t+(8QbESVEv_-7wDyGD+8BtJd z6}ro&!zdiuQeYyaT)d7cb&8qQnhQo%R-gyVRcTF&N?p&_C z>MB;PSzU&Yl-o5*lM=(2rYM+}0cO*(ps2I7-;_%h#56TM8F1{K@BZI*Aike3YfjmhsOWJJ`Kz7tVPMD-^UE7-5Vuz)agZ(DiHugeFFsCbDqx z5-z#?a=iBl!irN*C9CoL*3H~^@4bYoLY(J?ty_6%`*w~!?l`Xa`Z~71@H|c&hB0sc z0-7e0Qkqe3Mmh+i^wVspD_OAp#TPj+ImA~LtIn7?mrhJf@Uy$`LY&~82O!4C&+opA z_1Axg!;UzD)7Fg7So7}WBrpB_MVPixYjq$?mY!f$RZ~?pp$a&0WX&ACXc4EJz8dGG zYc~7$?`Pv!>WN2337FXI&#Q zmA|e9Ie5;Y>*KSzjJDR;D5bkdJ>ut%9sY*EWl~E16w+v{o>q!K@I9^BY0f1ha<;5_ zUBLAIlx+(lD-G~wsJQox}bn<&Uu(q8voau6Wap7+k5wHKKzIeeaX~*tD5U_E^yPP*D3h_ z7mS(mm`;U`iqJ4;z0?P{jp>#bE?h_xb(J{x3lTY8%JrTj*#|fowGl8gGu3Be6Th-| z@4vSl(&C5b&G(3X+LY_XxpQWLdPpA3?D3q_%~at}d*6Ix#_{~W4&E72%L1{300000 LNkvXXu0mjfY&Nig literal 0 HcmV?d00001 diff --git a/extension/public/icon48.png b/extension/public/icon48.png new file mode 100644 index 0000000000000000000000000000000000000000..a4ce736b5011838ea01bc6b9ccdcab783651ca0c GIT binary patch literal 3122 zcmV-249)Y2P)r5W|%6fN=hjYjXwrm zxBICdyfiHXE3UsDr?OJSZ4DvJ@*&`Tz?Te2&485jOZD%p88B=Ao`+6lAohtd9=4>b z#%F||{B+kY07AV_WHC;*`cTaBArL|)J~QAE5u9@%(hx0Y?-4E9F)W)FRef;jM#g;5<8H68~>c4&5S> z&DTjyql4d=nCz_#n)JRehin39^7>Glexzc(qIEPXY@p$Lv{x5Lm-7w>$-RBQWw zzt8cB33~m0qhgWis7C}koendGx_Iv!_|V=jMkbEu{j6C!h0gpMP-v^5G96RH9L$)} z;a#`h!YyllhvDJjX=Qq3^e7Mi<2QNosUOno^#}7tOIiYSyItOK^G*EGZGXVbnKP%| zZ~WLX{^ei4%YQujJtii5h?C5L(j>i--e71$v}rjUW(f$~{Pr8Uef_#ACN%iWpFfXH zAGnY4@o{!P`y4Swy!Ry&)FxJR3a-EI8s7J38#639$uphT_nc)Qv28H8kak?_9l#n3R4@ryK@s+Vp-dykG%lN{R{7geJu_;heDOl7+nY zo{c26)9x3O@_TE3ml*q`7|~Q(MHO61jRb8Wz$|@3O%a2Xgrd_WrUbyYt=ss{ceWtT z;Yy7xTC|7{{`Ftsy{9`g#D_lgK|cJEk8=FA37`al-ZL_LHk&rRALl(WC3?LcpZetA zv48)80hqpF!|mL7;|&0Gx?N)I<9tA*gc&Uw6(dkbp+V;`AZa3&5&?MP$tU^mpZ=@_ zc^Sw)|H99iGj}d`ZF~@bKF8y` zzK?gg=Ag9e-Me>l&9&D85Mv~z1US@Hk NW`qHos+_m-c@biY#8ehS;wVBvC<;Od zfS{WA_P4jNc*$a}y80@P9v$PE-Mfh~A>GJM(uVu+^F0S`^^t+F;^|7sauTBT= zd>tyq`FdW|gsS00F;yrLL3@Ns9d#jLidc@lst{AwLKVw0QuX+d)vD@9G4Z+2ex5tu zb0^!kZR60vLxd0rKCoiN3f{4Lb-l;)KYN}pedz(54<&FcaNgBbn26MKW2r4I!@3NZ zP>X>&eCiYpV8oQ@mp-6%wv+nDIS(EtCwpxE@@5RfIZp@$3m3kHJMOxZWJ*eK`F;1|eaL>)k9_jupJ4RpQ94DpE|lu%->Sr_^HNGJztmh_XlFx4WFacAj5|}FHl#*G@wOq5$BZ9)6f! zuZM^=Frjkd>b#g*`KieRT3JA?0c=`M&9wG`m@;5XWBT<0MuDFtk;}e9U$i)c4yan^Q_NKSjcDwiGmw4oB4`ruyIZ>Gzk8gd9rI#;f z-uwjstXaF3iOEUE$Hr>MHdU4^UDiaQS_2LKX(Q6wi3e+e#yL}h@v$+^n>7o7rOPj8 z>GI2`!uK2*QsUV7IA8g~=SeXViVn`_RwQN|d+itx-hV$I_?r*Y9U20JwST;x>7FVI zjg5_!?Zql_F#vnaG@FoWlStY_lk&ul9rR;7Eq$el_ZK&Lb^TO)=6hE483zNEy?!1{87N%#~0lxAUBW zqTnduHg)gIODHcu@~E4%)3idebJN0I8A7+)5>BLyblED*+5*tp541v~w6dL=!6oY)gI0JJmC7 zbNBQPW!CQh=rkHgq?CU2+G}Ii9UB`<^(k}DpMPlxo#&i$vzzOtiUHSLaG#8UDOZ-K zzo(!6#^+QFm?}xrA&e^z?EmFnXx|fC=S5Y0r(xU8Y_^!TpoP|s+&J{fm^j17rvIP~ zI|hJe4x5Ltbn}4&`}Usn$T@QsywwM{MVwpS+?SOmH1$sm&ZSpd3!t=HoRtPrdR9%= z9X|A{z3pYwQgHtH7ZwPAD#AyBUzf5^K0yBK5s~T}uDV81GuwmkiOJ*kt?}{wr~hM* z_A_tZ`~s01V$}CJ@7D@4d}b@pnt^fC^v@}!2ZMJ{zxwK-c(Nt`3t4UqH5#vcP5=M^ M07*qoM6N<$f<1CT4gdfE literal 0 HcmV?d00001 diff --git a/extension/public/manifest.json b/extension/public/manifest.json new file mode 100644 index 00000000000..6bf1cd0d1ae --- /dev/null +++ b/extension/public/manifest.json @@ -0,0 +1,25 @@ +{ + "manifest_version": 3, + "name": "AnythingLLM", + "version": "1.0.0", + "description": "Save selected text to AnythingLLM", + "icons": { + "16": "icon16.png", + "32": "icon32.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "permissions": ["contextMenus", "activeTab", "storage"], + "background": { + "service_worker": "background.js" + }, + "action": { + "default_popup": "index.html", + "default_icon": { + "16": "icon16.png", + "32": "icon32.png", + "48": "icon48.png", + "128": "icon128.png" + } + } +} diff --git a/extension/src/App.jsx b/extension/src/App.jsx new file mode 100644 index 00000000000..0d9e3f1a291 --- /dev/null +++ b/extension/src/App.jsx @@ -0,0 +1,30 @@ +import React, { useState, useEffect } from "react"; +import Config from "./components/Config"; +import AnythingLLMLogo from "./media/anything-llm.png"; + +function App() { + const [apiKey, setApiKey] = useState(""); + + useEffect(() => { + chrome.storage.sync.get(["apiKey"], (result) => { + if (result.apiKey) { + setApiKey("********"); + } + }); + }, []); + + return ( +
+ AnythingLLM Logo +
+

+ Select text on any webpage, right-click, and choose "Save to + AnythingLLM". +

+ +
+
+ ); +} + +export default App; diff --git a/extension/src/components/Config.jsx b/extension/src/components/Config.jsx new file mode 100644 index 00000000000..e44e2363720 --- /dev/null +++ b/extension/src/components/Config.jsx @@ -0,0 +1,60 @@ +import React, { useState, useEffect } from "react"; + +export default function Config({ apiKey, setApiKey }) { + const [inputApiKey, setInputApiKey] = useState(""); + const [saveStatus, setSaveStatus] = useState(""); + const [isModified, setIsModified] = useState(false); + + useEffect(() => { + setInputApiKey(apiKey); + }, [apiKey]); + + const handleInputChange = (e) => { + setInputApiKey(e.target.value); + setIsModified(true); + }; + + const handleSave = () => { + chrome.storage.sync.set({ apiKey: inputApiKey }, () => { + setApiKey("********"); + setSaveStatus("API Key saved successfully!"); + setIsModified(false); + setTimeout(() => setSaveStatus(""), 3000); + }); + }; + + return ( +
+ +
+ + +
+ {saveStatus && ( +

{saveStatus}

+ )} +
+ ); +} diff --git a/extension/src/index.css b/extension/src/index.css new file mode 100644 index 00000000000..63ec9e0183e --- /dev/null +++ b/extension/src/index.css @@ -0,0 +1,31 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} diff --git a/extension/src/main.jsx b/extension/src/main.jsx new file mode 100644 index 00000000000..b91620d35e1 --- /dev/null +++ b/extension/src/main.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + +); diff --git a/extension/src/media/anything-llm.png b/extension/src/media/anything-llm.png new file mode 100644 index 0000000000000000000000000000000000000000..341d21b6cea9a1b7b8ed431920931c55f4d1c0c2 GIT binary patch literal 6324 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 literal 0 HcmV?d00001 diff --git a/extension/tailwind.config.js b/extension/tailwind.config.js new file mode 100644 index 00000000000..f5a68e2221d --- /dev/null +++ b/extension/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {} + }, + plugins: [] +} diff --git a/extension/vite.config.js b/extension/vite.config.js new file mode 100644 index 00000000000..6bb87dfb6e3 --- /dev/null +++ b/extension/vite.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from "vite" +import react from "@vitejs/plugin-react" + +export default defineConfig({ + plugins: [react()], + build: { + rollupOptions: { + input: { + main: "index.html" + } + }, + outDir: "dist" + } +}) From cbe3115731acc4a555101b7521d78ba0eed184cf Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Tue, 13 Aug 2024 17:35:02 -0700 Subject: [PATCH 02/37] wip browser extension backend --- server/endpoints/browserExtension.js | 119 ++++++++++++++++++ server/index.js | 4 + server/models/browserExtensionApiKey.js | 72 +++++++++++ .../20240813223720_init/migration.sql | 10 ++ server/prisma/schema.prisma | 7 ++ .../middleware/validBrowserExtensionApiKey.js | 29 +++++ 6 files changed, 241 insertions(+) create mode 100644 server/endpoints/browserExtension.js create mode 100644 server/models/browserExtensionApiKey.js create mode 100644 server/prisma/migrations/20240813223720_init/migration.sql create mode 100644 server/utils/middleware/validBrowserExtensionApiKey.js diff --git a/server/endpoints/browserExtension.js b/server/endpoints/browserExtension.js new file mode 100644 index 00000000000..afd35b5e7d5 --- /dev/null +++ b/server/endpoints/browserExtension.js @@ -0,0 +1,119 @@ +const { Workspace } = require("../models/workspace"); +const { BrowserExtensionApiKey } = require("../models/browserExtensionApiKey"); +const { Document } = require("../models/documents"); +const { + validBrowserExtensionApiKey, +} = require("../utils/middleware/validBrowserExtensionApiKey"); +const { CollectorApi } = require("../utils/collectorApi"); +const { reqBody } = require("../utils/http"); + +function browserExtensionEndpoints(app) { + if (!app) return; + + app.get( + "/browser-extension/check", + [validBrowserExtensionApiKey], + async (request, response) => { + try { + const workspaces = await Workspace.where(); + response.status(200).json({ connected: true, workspaces }); + } catch (error) { + console.error(error); + response + .status(500) + .json({ connected: false, error: "Failed to fetch workspaces" }); + } + } + ); + + app.post("/browser-extension/register", async (request, response) => { + try { + const { apiKey, error } = await BrowserExtensionApiKey.create(); + if (error) throw new Error(error); + response.status(200).json({ apiKey: apiKey.key }); + } catch (error) { + console.error(error); + response + .status(500) + .json({ error: "Failed to register browser extension" }); + } + }); + + app.post( + "/browser-extension/unregister", + [validBrowserExtensionApiKey], + async (request, response) => { + try { + const auth = request.header("Authorization"); + const bearerKey = auth ? auth.split(" ")[1] : null; + const { success, error } = + await BrowserExtensionApiKey.delete(bearerKey); + if (!success) throw new Error(error); + response.status(200).json({ success: true }); + } catch (error) { + console.error(error); + response + .status(500) + .json({ error: "Failed to unregister browser extension" }); + } + } + ); + + app.get( + "/browser-extension/workspaces", + [validBrowserExtensionApiKey], + async (request, response) => { + try { + const workspaces = await Workspace.where(); + response.status(200).json({ workspaces }); + } catch (error) { + console.error(error); + response.status(500).json({ error: "Failed to fetch workspaces" }); + } + } + ); + + // TODO: Fix this, it always returns "Failed to embed content" + app.post( + "/browser-extension/embed-content", + [validBrowserExtensionApiKey], + async (request, response) => { + try { + const { workspaceId, textContent, metadata } = reqBody(request); + const workspace = await Workspace.get({ id: workspaceId }); + if (!workspace) { + response.status(404).json({ error: "Workspace not found" }); + return; + } + + const Collector = new CollectorApi(); + const { success, reason, documents } = await Collector.processRawText( + textContent, + metadata + ); + + if (!success) { + response.status(500).json({ success: false, error: reason }); + return; + } + + const { failedToEmbed = [], errors = [] } = await Document.addDocuments( + workspace, + documents + ); + + if (failedToEmbed.length > 0) { + response.status(500).json({ success: false, error: errors[0] }); + return; + } + + response.status(200).json({ success: true }); + } catch (error) { + console.error(error); + response.status(500).json({ error: "Failed to embed content" }); + } + } + ); +} + +module.exports = { browserExtensionEndpoints }; diff --git a/server/index.js b/server/index.js index 757f353449d..041d34c9562 100644 --- a/server/index.js +++ b/server/index.js @@ -24,6 +24,7 @@ const { workspaceThreadEndpoints } = require("./endpoints/workspaceThreads"); const { documentEndpoints } = require("./endpoints/document"); const { agentWebsocket } = require("./endpoints/agentWebsocket"); const { experimentalEndpoints } = require("./endpoints/experimental"); +const { browserExtensionEndpoints } = require("./endpoints/browserExtension"); const app = express(); const apiRouter = express.Router(); const FILE_LIMIT = "3GB"; @@ -62,6 +63,9 @@ developerEndpoints(app, apiRouter); // Externally facing embedder endpoints embeddedEndpoints(apiRouter); +// Externally facing browser extension endpoints +browserExtensionEndpoints(apiRouter); + if (process.env.NODE_ENV !== "development") { const { MetaGenerator } = require("./utils/boot/MetaGenerator"); const IndexPage = new MetaGenerator(); diff --git a/server/models/browserExtensionApiKey.js b/server/models/browserExtensionApiKey.js new file mode 100644 index 00000000000..626a024e453 --- /dev/null +++ b/server/models/browserExtensionApiKey.js @@ -0,0 +1,72 @@ +const prisma = require("../utils/prisma"); +const uuidAPIKey = require("uuid-apikey"); + +const BrowserExtensionApiKey = { + tablename: "browser_extension_api_keys", + writable: [], + + makeSecret: () => { + return `brx-${uuidAPIKey.create().apiKey}`; + }, + + create: async function () { + try { + const apiKey = await prisma.browser_extension_api_keys.create({ + data: { + key: this.makeSecret(), + }, + }); + return { apiKey, error: null }; + } catch (error) { + console.error("Failed to create browser extension API key", error); + return { apiKey: null, error: error.message }; + } + }, + + validate: async function (key) { + if (!key.startsWith("brx-")) return false; + const apiKey = await prisma.browser_extension_api_keys.findUnique({ + where: { key }, + }); + return !!apiKey; + }, + + get: async function (clause = {}) { + try { + const apiKey = await prisma.browser_extension_api_keys.findFirst({ + where: clause, + }); + return apiKey; + } catch (error) { + console.error("FAILED TO GET BROWSER EXTENSION API KEY.", error.message); + return null; + } + }, + + delete: async function (key) { + try { + await prisma.browser_extension_api_keys.delete({ + where: { key }, + }); + return { success: true, error: null }; + } catch (error) { + console.error("Failed to delete browser extension API key", error); + return { success: false, error: error.message }; + } + }, + + where: async function (clause = {}, limit) { + try { + const apiKeys = await prisma.browser_extension_api_keys.findMany({ + where: clause, + take: limit, + }); + return apiKeys; + } catch (error) { + console.error("FAILED TO GET BROWSER EXTENSION API KEYS.", error.message); + return []; + } + }, +}; + +module.exports = { BrowserExtensionApiKey }; diff --git a/server/prisma/migrations/20240813223720_init/migration.sql b/server/prisma/migrations/20240813223720_init/migration.sql new file mode 100644 index 00000000000..a2695a28b78 --- /dev/null +++ b/server/prisma/migrations/20240813223720_init/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "browser_extension_api_keys" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "key" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "browser_extension_api_keys_key_key" ON "browser_extension_api_keys"("key"); diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index f385e66f45f..0edd9f57ae1 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -297,3 +297,10 @@ model document_sync_executions { createdAt DateTime @default(now()) queue document_sync_queues @relation(fields: [queueId], references: [id], onDelete: Cascade) } + +model browser_extension_api_keys { + id Int @id @default(autoincrement()) + key String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} \ No newline at end of file diff --git a/server/utils/middleware/validBrowserExtensionApiKey.js b/server/utils/middleware/validBrowserExtensionApiKey.js new file mode 100644 index 00000000000..5fd6da53ca0 --- /dev/null +++ b/server/utils/middleware/validBrowserExtensionApiKey.js @@ -0,0 +1,29 @@ +const { + BrowserExtensionApiKey, +} = require("../../models/browserExtensionApiKey"); +const { SystemSettings } = require("../../models/systemSettings"); + +async function validBrowserExtensionApiKey(request, response, next) { + const multiUserMode = await SystemSettings.isMultiUserMode(); + response.locals.multiUserMode = multiUserMode; + + const auth = request.header("Authorization"); + const bearerKey = auth ? auth.split(" ")[1] : null; + if (!bearerKey) { + response.status(403).json({ + error: "No valid API key found.", + }); + return; + } + + if (!(await BrowserExtensionApiKey.validate(bearerKey))) { + response.status(403).json({ + error: "No valid API key found.", + }); + return; + } + + next(); +} + +module.exports = { validBrowserExtensionApiKey }; From 67d0e06c0badaba5d8dd3e0641c91942fd4562cc Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Thu, 15 Aug 2024 14:31:58 -0700 Subject: [PATCH 03/37] wip frontend browser extension settings --- frontend/src/App.jsx | 7 + .../src/components/SettingsSidebar/index.jsx | 6 + frontend/src/locales/de/common.js | 1 + frontend/src/locales/en/common.js | 1 + frontend/src/locales/es/common.js | 1 + frontend/src/locales/fr/common.js | 1 + frontend/src/locales/it/common.js | 1 + frontend/src/locales/ko/common.js | 1 + frontend/src/locales/pt_BR/common.js | 1 + frontend/src/locales/ru/common.js | 1 + frontend/src/locales/zh/common.js | 1 + .../BrowserExtension/index.jsx | 343 ++++++++++++++++++ frontend/src/utils/paths.js | 3 + 13 files changed, 368 insertions(+) create mode 100644 frontend/src/pages/GeneralSettings/BrowserExtension/index.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3737541f240..efc4920d084 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -49,6 +49,9 @@ const GeneralVectorDatabase = lazy( () => import("@/pages/GeneralSettings/VectorDatabase") ); const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security")); +const GeneralBrowserExtension = lazy( + () => import("@/pages/GeneralSettings/BrowserExtension") +); const WorkspaceSettings = lazy(() => import("@/pages/WorkspaceSettings")); const EmbedConfigSetup = lazy( () => import("@/pages/GeneralSettings/EmbedConfigs") @@ -157,6 +160,10 @@ export default function App() { path="/settings/api-keys" element={} /> + } + /> } diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index 9fa6fd6112d..54d7d47f30d 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -332,6 +332,12 @@ const SidebarOptions = ({ user = null, t }) => ( flex: true, roles: ["admin"], }, + { + btnText: t("settings.browser-extension"), + href: paths.settings.broswerExtension(), + flex: true, + roles: ["admin"], + }, ]} />