├── .gitignore ├── README.md ├── background.js ├── content.js ├── demo.gif ├── extension ├── .gitignore ├── README.md ├── dist │ ├── index.html │ ├── main.js │ └── style.css ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── vite.svg ├── src │ ├── App.svelte │ ├── app.css │ ├── lib │ │ ├── apis │ │ │ └── index.js │ │ ├── components │ │ │ └── SpotlightSearch.svelte │ │ └── utils │ │ │ └── index.js │ ├── main.ts │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── images ├── icon-128.png ├── icon-16.png ├── icon-32.png └── icon-48.png └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open WebUI Chrome Extension 👋 2 | 3 |  4 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { 2 | console.log(request, sender); 3 | const id = sender.tab.id; 4 | if (request.action == "getSelection") { 5 | chrome.scripting 6 | .executeScript({ 7 | target: { tabId: id, allFrames: true }, 8 | func: () => { 9 | return window.getSelection().toString(); 10 | }, 11 | }) 12 | .then((res) => { 13 | console.log(res); 14 | sendResponse({ data: res[0]["result"] }); 15 | }); 16 | } else if (request.action == "writeText") { 17 | function writeTextToInput(text) { 18 | const activeElement = document.activeElement; 19 | if ( 20 | activeElement && 21 | (activeElement.tagName === "INPUT" || 22 | activeElement.tagName === "TEXTAREA") 23 | ) { 24 | activeElement.value = `${activeElement.value}${text}`; 25 | 26 | if (activeElement.tagName === "TEXTAREA") { 27 | activeElement.scrollTop = activeElement.scrollHeight; 28 | } 29 | } else { 30 | console.warn("No active input or textarea field found."); 31 | } 32 | } 33 | chrome.scripting.executeScript({ 34 | target: { tabId: id, allFrames: true }, 35 | func: writeTextToInput, 36 | args: [request.text], 37 | }); 38 | sendResponse({}); 39 | } else { 40 | sendResponse({}); 41 | } 42 | 43 | return true; 44 | }); 45 | -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | // Create a div to host the React app 2 | const appDiv = document.createElement("div"); 3 | appDiv.id = "extension-app"; 4 | document.body.appendChild(appDiv); 5 | 6 | // Function to inject a script 7 | function injectScript(file, node) { 8 | const th = document.getElementsByTagName(node)[0]; 9 | const s = document.createElement("script"); 10 | s.setAttribute("type", "text/javascript"); 11 | s.setAttribute("src", file); 12 | th.appendChild(s); 13 | } 14 | 15 | // Function to inject a CSS file 16 | function injectCSS(file) { 17 | const link = document.createElement("link"); 18 | link.href = file; 19 | link.type = "text/css"; 20 | link.rel = "stylesheet"; 21 | document.getElementsByTagName("head")[0].appendChild(link); 22 | } 23 | 24 | // Inject the CSS and JS files 25 | injectCSS(chrome.runtime.getURL("extension/dist/style.css")); 26 | // injectScript(chrome.runtime.getURL("extension/dist/main.js"), "body"); 27 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-webui/extension/635b90c685649806b2b1e06a39c6e3e2479195d5/demo.gif -------------------------------------------------------------------------------- /extension/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | _old 16 | 17 | # Editor directories and files 18 | .vscode 19 | .vscode/* 20 | !.vscode/extensions.json 21 | .idea 22 | .DS_Store 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | -------------------------------------------------------------------------------- /extension/README.md: -------------------------------------------------------------------------------- 1 | # Svelte + TS + Vite 2 | 3 | This template should help get you started developing with Svelte and TypeScript in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). 8 | 9 | ## Need an official Svelte framework? 10 | 11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. 12 | 13 | ## Technical considerations 14 | 15 | **Why use this over SvelteKit?** 16 | 17 | - It brings its own routing solution which might not be preferable for some users. 18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. 19 | 20 | This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. 21 | 22 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. 23 | 24 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** 25 | 26 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. 27 | 28 | **Why include `.vscode/extensions.json`?** 29 | 30 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. 31 | 32 | **Why enable `allowJs` in the TS template?** 33 | 34 | While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant. 35 | 36 | **Why is HMR not preserving my local component state?** 37 | 38 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). 39 | 40 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. 41 | 42 | ```ts 43 | // store.ts 44 | // An extremely simple external store 45 | import { writable } from 'svelte/store' 46 | export default writable(0) 47 | ``` 48 | -------------------------------------------------------------------------------- /extension/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |p?1:ri?1:0}),console.log(o),o},He=async(e="",t={},l="http://localhost:8080")=>{const n=new AbortController;let o=null;const r=await fetch(`${l}/chat/completions`,{signal:n.signal,method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(i=>(console.log(i),o=i,null));if(o)throw o;return[r,n]},ze=e=>{let t="";return new TransformStream({transform(l,n){t+=l;const o=t.split(e);o.slice(0,-1).forEach(r=>n.enqueue(r)),t=o[o.length-1]},flush(l){t&&l.enqueue(t)}})};function ne(e,t,l){const n=e.slice();return n[6]=t[l],n}function oe(e){let t,l,n;function o(s,p){return s[1]?Pe:Ke}let r=o(e),i=r(e);return{c(){t=m("div"),i.c(),u(t,"class","tlwd-fixed tlwd-top-0 tlwd-right-0 tlwd-left-0 tlwd-bottom-0 tlwd-w-full tlwd-min-h-screen tlwd-h-screen tlwd-flex tlwd-justify-center tlwd-z-[9999999999] tlwd-overflow-hidden tlwd-overscroll-contain")},m(s,p){L(s,t,p),i.m(t,null),l||(n=S(t,"mousedown",e[15]),l=!0)},p(s,p){r===(r=o(s))&&i?i.p(s,p):(i.d(1),i=r(s),i&&(i.c(),i.m(t,null)))},d(s){s&&j(t),i.d(),l=!1,n()}}}function Ke(e){let t,l,n,o,r,i,s,p,d,h,a,c,y,f;return{c(){t=m("div"),l=m("div"),n=m("form"),o=m("div"),r=m("div"),r.innerHTML=``,i=C(),s=m("input"),p=C(),d=m("div"),h=m("div"),h.textContent="Press ⌘Space+Shift to toggle",a=C(),c=m("button"),c.innerHTML=``,u(r,"class","tlwd-flex tlwd-items-center"),u(s,"id","open-webui-search-input"),u(s,"placeholder","Search Open WebUI"),u(s,"class","tlwd-p-0 tlwd-m-0 tlwd-text-xl tlwd-w-full tlwd-font-medium tlwd-bg-transparent tlwd-border-none placeholder:tlwd-text-gray-500 tlwd-text-neutral-100 tlwd-outline-none"),u(s,"autocomplete","one-time-code"),u(o,"class","tlwd-flex tlwd-items-center tlwd-gap-2 tlwd-w-full"),u(h,"class","tlwd-text-right tlwd-text-[0.7rem] tlwd-p-0 tlwd-m-0 tlwd-text-neutral-300 tlwd-h-fit"),u(c,"class","tlwd-h-fit tlwd-flex tlwd-items-center tlwd-bg-transparent tlwd-text-neutral-100 tlwd-cursor-pointer tlwd-p-0 tlwd-m-0 tlwd-outline-none tlwd-border-none"),u(c,"type","button"),u(d,"class","tlwd-flex tlwd-justify-end tlwd-gap-1 tlwd-items-center"),u(n,"class","tlwd-text-gray-200 tlwd-w-full tlwd-p-0 tlwd-m-0"),u(n,"autocomplete","off"),u(l,"class","tlwd-w-full tlwd-flex tlwd-flex-col tlwd-justify-between tlwd-py-2.5 tlwd-px-3.5 tlwd-rounded-2xl tlwd-outline tlwd-outline-1 tlwd-outline-gray-850 tlwd-backdrop-blur-3xl tlwd-bg-gray-850/70 shadow-4xl modal-animation"),u(t,"class","tlwd-m-auto tlwd-max-w-xl tlwd-w-full tlwd-pb-32")},m(k,E){L(k,t,E),w(t,l),w(l,n),w(n,o),w(o,r),w(o,i),w(o,s),M(s,e[4]),w(n,p),w(n,d),w(d,h),w(d,a),w(d,c),y||(f=[S(s,"input",e[13]),S(c,"click",e[14]),S(n,"submit",e[7]),S(n,"mousedown",Ue)],y=!0)},p(k,E){E&16&&s.value!==k[4]&&M(s,k[4])},d(k){k&&j(t),y=!1,A(f)}}}function Pe(e){let t,l,n,o,r,i,s,p,d,h,a,c,y,f,k,E,H,b=e[5]&&e[5].length>0&&re(e);return{c(){t=m("div"),l=m("div"),n=m("form"),o=m("div"),r=m("div"),r.innerHTML=``,i=C(),s=m("input"),p=C(),d=m("div"),h=m("div"),h.innerHTML=``,a=C(),c=m("input"),y=C(),f=m("button"),f.innerHTML=``,k=C(),b&&b.c(),u(r,"class","tlwd-flex tlwd-items-center"),u(s,"id","open-webui-url-input"),u(s,"placeholder","Open WebUI URL"),u(s,"class","tlwd-p-0 tlwd-m-0 tlwd-text-xl tlwd-w-full tlwd-font-medium tlwd-bg-transparent tlwd-border-none placeholder:tlwd-text-gray-500 tlwd-text-neutral-100 tlwd-outline-none"),u(s,"autocomplete","one-time-code"),s.required=!0,u(o,"class","tlwd-flex tlwd-items-center tlwd-gap-2 tlwd-w-full"),u(h,"class","tlwd-flex tlwd-items-center"),u(c,"placeholder","Open WebUI API Key"),u(c,"class","tlwd-p-0 tlwd-m-0 tlwd-text-xl tlwd-w-full tlwd-font-medium tlwd-bg-transparent tlwd-border-none placeholder:tlwd-text-gray-500 tlwd-text-neutral-100 tlwd-outline-none"),u(c,"autocomplete","one-time-code"),c.required=!0,u(f,"class","tlwd-flex tlwd-items-center tlwd-bg-transparent tlwd-text-neutral-100 tlwd-cursor-pointer tlwd-p-0 tlwd-m-0 tlwd-outline-none tlwd-border-none"),u(f,"type","button"),u(d,"class","tlwd-flex tlwd-items-center tlwd-gap-2 tlwd-w-full tlwd-mt-2"),u(n,"class","tlwd-text-gray-200 tlwd-w-full tlwd-p-0 tlwd-m-0"),u(n,"autocomplete","off"),u(l,"class","tlwd-w-full tlwd-flex tlwd-flex-col tlwd-justify-between tlwd-py-2.5 tlwd-px-3.5 tlwd-rounded-2xl tlwd-outline tlwd-outline-1 tlwd-outline-gray-850 tlwd-backdrop-blur-3xl tlwd-bg-gray-850/70 shadow-4xl modal-animation"),u(t,"class","tlwd-m-auto tlwd-max-w-sm tlwd-w-full tlwd-pb-32")},m(x,g){L(x,t,g),w(t,l),w(l,n),w(n,o),w(o,r),w(o,i),w(o,s),M(s,e[2]),w(n,p),w(n,d),w(d,h),w(d,a),w(d,c),M(c,e[3]),w(d,y),w(d,f),w(n,k),b&&b.m(n,null),E||(H=[S(s,"input",e[9]),S(c,"input",e[10]),S(f,"click",e[11]),S(n,"submit",e[8]),S(n,"mousedown",De)],E=!0)},p(x,g){g&4&&s.value!==x[2]&&M(s,x[2]),g&8&&c.value!==x[3]&&M(c,x[3]),x[5]&&x[5].length>0?b?b.p(x,g):(b=re(x),b.c(),b.m(n,null)):b&&(b.d(1),b=null)},d(x){x&&j(t),b&&b.d(),E=!1,A(H)}}}function re(e){let t,l,n,o,r,i,s,p,d,h=te(e[5]),a=[];for(let c=0;c