murmur-rag 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cdn/murmur.js +5 -5
- package/dist/lib/chunks/{index-v9expxwm.js → index-0x7gav29.js} +19 -4
- package/dist/lib/embed.js +2 -2
- package/dist/lib/widget/index.js +2 -2
- package/dist/types/core/murmur.d.ts +2 -2
- package/dist/types/embed.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/indexer/extract.d.ts +1 -1
- package/dist/types/indexer/files.d.ts +1 -1
- package/dist/types/indexer/index.d.ts +4 -4
- package/dist/types/rag/chunk.d.ts +1 -1
- package/dist/types/rag/ground.d.ts +1 -1
- package/dist/types/rag/index.d.ts +4 -4
- package/dist/types/rag/retriever.d.ts +1 -1
- package/dist/types/widget/element.d.ts +4 -3
- package/dist/types/widget/index.d.ts +6 -6
- package/dist/types/widget/session.d.ts +5 -5
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -90,7 +90,8 @@ No server, no analytics. The question and the matching passages go to the AI pro
|
|
|
90
90
|
bun install
|
|
91
91
|
bun run dev # build, then serve on http://localhost:4317/murmur/
|
|
92
92
|
bun run test # unit tests
|
|
93
|
-
bun run test:e2e # Playwright
|
|
93
|
+
bun run test:e2e # Playwright against a fake OpenAI-compatible endpoint, axe WCAG 2.2 AA scans
|
|
94
|
+
bun run test:package # packs, installs and checks the CLI, Node imports and types
|
|
94
95
|
bun run typecheck && bun run lint
|
|
95
96
|
```
|
|
96
97
|
|
package/dist/cdn/murmur.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{G,gn,kt}from"./embed-2xm309sb.js";gn(import.meta.url,["
|
|
2
|
-
var
|
|
1
|
+
import{G,gn,kt}from"./embed-2xm309sb.js";gn(import.meta.url,["d1gkys45","11c9cspb","e0j28zmb","zrmby2xc","5vkc01ns","k9mmdcz5","e5entetm","2xm309sb","hn0r8fmg","q6y6vgv1","n7jskdxd","we24qtes","t87kf8t0","dhy4fp4c","w7nkjk5e","qha5xyx4","16bekg8b","za7hc1fv","kk9hgxdc","sgjh2kg2","59r4jck6"],[["./murmur.js",7],["./index-11c9cspb.js",8],["./chunk-e0j28zmb.js",9],["./index-zrmby2xc.js",9],["./murmur-5vkc01ns.js",10,11,7],["./retriever-k9mmdcz5.js",11],["./index-e5entetm.js",8],["./embed-2xm309sb.js"],["./embed-hn0r8fmg.js",7],["./embed-q6y6vgv1.js"],["./embed-n7jskdxd.js",7,19],["./embed-we24qtes.js",7],["./browser-t87kf8t0.js"],["./openai-dhy4fp4c.js",8,7,19],["./anthropic-w7nkjk5e.js",8,20,19],["./google-qha5xyx4.js",8,20,19],["./mistral-16bekg8b.js",8,19],["./openai-compatible-za7hc1fv.js",8,19],["./index-kk9hgxdc.js",10],["./embed-sgjh2kg2.js"],["./embed-59r4jck6.js",19]],0);
|
|
2
|
+
var F={open:"Ask a question",close:"Close",title:"Ask this site",greeting:"Hi! Ask me anything about this site. I answer from its pages and link to them.",placeholder:"Your question…",send:"Send",stop:"Stop",settings:"AI settings",back:"Back to chat",sources:"Sources",notFound:"Nothing on this site matched. Try other words?",thinking:"Looking through the site…",newChat:"New conversation",poweredBy:"Answers from this site's pages, by murmur",errorAuth:"The key was refused. Check it in the AI settings.",errorNetwork:"No answer from the AI provider: key refused or network down.",errorRate:"The AI provider is busy or rate-limited. Try again in a moment.",errorOther:"The answer failed:",retry:"Try again",setupTitle:"Connect an AI model",setupIntro:"This site lets you use your own AI key. It stays in your browser, encrypted, and is only sent to the provider you pick.",provider:"Provider",model:"Model",apiKey:"API key",apiKeyHint:"Sent only to the provider, never to this site.",remember:"Remember on this device",local:"Local server (Ollama, LM Studio)",localHint:"The server must allow this site's origin (CORS).",endpoint:"Server URL",test:"Test",testing:"Testing…",testOk:"Connected.",testFail:"Connection failed:",save:"Save and chat",forget:"Forget my key",loadingCatalog:"Loading models…",catalogError:"Could not load the model list.",searchModels:"Search models",recommended:"Recommended",otherProviders:"Other providers"},Z={open:"Poser une question",close:"Fermer",title:"Demandez au site",greeting:"Bonjour ! Posez-moi une question sur ce site. Je réponds à partir de ses pages, avec les liens.",placeholder:"Votre question…",send:"Envoyer",stop:"Arrêter",settings:"Réglages IA",back:"Retour au chat",sources:"Sources",notFound:"Rien sur ce site ne correspond. Essayez d'autres mots ?",thinking:"Je cherche dans le site…",newChat:"Nouvelle conversation",poweredBy:"Réponses tirées des pages de ce site, par murmur",errorAuth:"La clé a été refusée. Vérifiez-la dans les réglages IA.",errorNetwork:"Pas de réponse du fournisseur d'IA : clé refusée ou réseau coupé.",errorRate:"Le fournisseur d'IA est saturé ou limite les appels. Réessayez dans un instant.",errorOther:"La réponse a échoué :",retry:"Réessayer",setupTitle:"Connecter un modèle d'IA",setupIntro:"Ce site vous laisse utiliser votre propre clé d'IA. Elle reste dans votre navigateur, chiffrée, et n'est envoyée qu'au fournisseur choisi.",provider:"Fournisseur",model:"Modèle",apiKey:"Clé API",apiKeyHint:"Envoyée uniquement au fournisseur, jamais à ce site.",remember:"Mémoriser sur cet appareil",local:"Serveur local (Ollama, LM Studio)",localHint:"Le serveur doit autoriser l'origine de ce site (CORS).",endpoint:"URL du serveur",test:"Tester",testing:"Test…",testOk:"Connecté.",testFail:"Échec de la connexion :",save:"Enregistrer et discuter",forget:"Oublier ma clé",loadingCatalog:"Chargement des modèles…",catalogError:"Impossible de charger la liste des modèles.",searchModels:"Chercher un modèle",recommended:"Recommandés",otherProviders:"Autres fournisseurs"},V={en:F,fr:Z};function j(...e){for(let n of e){let i=n?.toLowerCase().split(/[-_]/)[0];if(i&&i in V)return i}return"en"}function P(e,n={}){return{...V[e]??F,...n}}var Q={chat:'<path d="M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v7a2.5 2.5 0 0 1-2.5 2.5H10l-4.2 3.4c-.5.4-1.3 0-1.3-.6V6.5Z"/><path d="M8.5 9h7M8.5 12h4.5"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',send:'<path d="M12 19V5M6 11l6-6 6 6"/>',stop:'<rect x="7" y="7" width="10" height="10" rx="1.5"/>',settings:'<path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/>',reset:'<path d="M5 12a7 7 0 1 0 2.1-5"/><path d="M5 4.5V8h3.5"/>',back:'<path d="M14.5 6 8.5 12l6 6"/>',check:'<path d="m5.5 12.5 4 4 9-9"/>',key:'<circle cx="8" cy="15" r="3.5"/><path d="m10.5 12.5 8-8M16 7l2 2M14 9l1.5 1.5"/>'};function R(e,n){let i=e.createElement("template");return i.innerHTML=`<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${Q[n]??""}</svg>`,i.content.firstElementChild}var X=/^(https?:\/\/|\/|#|\.\/|mailto:)/i;function k(e,n,i,t){let s=/(`[^`]+`)|(\*\*[^*]+\*\*)|(\*[^*\s][^*]*\*|_[^_\s][^_]*_)|(\[[^\]]+\]\([^)\s]+\))|(\[\d{1,2}\])/g,a=0;for(let c of n.matchAll(s)){let p=c.index??0;if(p>a)t.appendChild(e.createTextNode(n.slice(a,p)));a=p+c[0].length;let o=c[0];if(c[1]){let l=e.createElement("code");l.textContent=o.slice(1,-1),t.appendChild(l)}else if(c[2]){let l=e.createElement("strong");k(e,o.slice(2,-2),i,l),t.appendChild(l)}else if(c[3]){let l=e.createElement("em");k(e,o.slice(1,-1),i,l),t.appendChild(l)}else if(c[4]){let l=o.indexOf("]("),d=o.slice(1,l),u=o.slice(l+2,-1);if(X.test(u)){let h=e.createElement("a");if(h.href=u,h.textContent=d,/^https?:/i.test(u))h.target="_blank",h.rel="noopener noreferrer";t.appendChild(h)}else t.appendChild(e.createTextNode(d))}else{let l=i(Number(o.slice(1,-1)));t.appendChild(l??e.createTextNode(o))}}if(a<n.length)t.appendChild(e.createTextNode(n.slice(a)))}function U(e,n,i){let t=e.ownerDocument,s=t.createDocumentFragment(),a=n.replace(/\r\n?/g,`
|
|
3
3
|
`).split(`
|
|
4
|
-
`),c=[],p=null,o=()=>{if(!c.length)return;let d=t.createElement("p");k(t,c.join(" "),
|
|
5
|
-
`),x.appendChild(M),s.appendChild(x);continue}let b=/^\s*(?:([-*+])|(\d+)[.)])\s+(.*)$/.exec(u);if(b){o();let m=b[1]?"ul":"ol";if(!p||p.tagName.toLowerCase()!==m)p=t.createElement(m),s.appendChild(p);let x=t.createElement("li");k(t,b[3]??"",
|
|
4
|
+
`),c=[],p=null,o=()=>{if(!c.length)return;let d=t.createElement("p");k(t,c.join(" "),i,d),s.appendChild(d),c=[]},l=()=>{p=null};for(let d=0;d<a.length;d++){let u=a[d]??"";if(/^\s*```/.exec(u)){o(),l();let m=[];for(d++;d<a.length&&!/^\s*```/.test(a[d]??"");d++)m.push(a[d]??"");let x=t.createElement("pre");x.tabIndex=0;let M=t.createElement("code");M.textContent=m.join(`
|
|
5
|
+
`),x.appendChild(M),s.appendChild(x);continue}let b=/^\s*(?:([-*+])|(\d+)[.)])\s+(.*)$/.exec(u);if(b){o();let m=b[1]?"ul":"ol";if(!p||p.tagName.toLowerCase()!==m)p=t.createElement(m),s.appendChild(p);let x=t.createElement("li");k(t,b[3]??"",i,x),p.appendChild(x);continue}let f=/^\s*#{1,6}\s+(.*)$/.exec(u);if(f){o(),l();let m=t.createElement("p"),x=t.createElement("strong");k(t,f[1]??"",i,x),m.appendChild(x),s.appendChild(m);continue}if(!u.trim()){o(),l();continue}if(p&&/^\s{2,}\S/.test(u)){let m=p.lastElementChild;if(m){m.appendChild(t.createTextNode(" ")),k(t,u.trim(),i,m);continue}}l(),c.push(u.trim())}o(),e.replaceChildren(s)}class O{options;turns=[];engine=null;storePromise=null;historyKey;constructor(e){this.options=e;this.historyKey=`${e.storageKey??"murmur"}-chat:${location.pathname.split("/")[1]??""}`;try{let n=sessionStorage.getItem(this.historyKey);if(n)this.turns=JSON.parse(n)}catch{this.turns=[]}}get mode(){if(this.options.model)return"custom";return this.options.llm?.endpoint?"proxy":"visitor"}persist(){try{sessionStorage.setItem(this.historyKey,JSON.stringify(this.turns.slice(-30)))}catch{}}clear(){this.turns=[],this.persist()}store(){return this.storePromise??=(G("11c9cspb"),import("./index-11c9cspb.js")).then((e)=>{let n={};if(this.options.catalogUrl)n.catalogUrl=this.options.catalogUrl;if(this.options.storageKey)n.storageKey=this.options.storageKey;return e.visitorStore(this.options.llm??{},n)}),this.storePromise}async settings(){if(this.mode==="custom")return null;let e=await (G("11c9cspb"),import("./index-11c9cspb.js"));return e.resolveSettings(this.options.llm??{},this.mode==="visitor"?await this.store():null)}async needsSetup(){return this.mode==="visitor"&&!await this.settings()}async loadIndex(){let{index:e}=this.options;if(e&&typeof e!=="string")return e;let{isMurmurIndex:n}=await (G("e0j28zmb"),import("./chunk-e0j28zmb.js"));if(e){let t=await fetch(new URL(e,location.href));if(!t.ok)throw Error(`murmur: index ${t.status}`);let s=await t.json();if(!n(s))throw Error("murmur: not a murmur index");return s}let{indexDocument:i}=await (G("zrmby2xc"),import("./index-zrmby2xc.js"));return i(document,location.pathname+location.search)}createEngine(){return(async()=>{let[{createMurmur:e},n]=await Promise.all([(G("5vkc01ns"),import("./murmur-5vkc01ns.js")),this.options.retriever??(G("k9mmdcz5"),import("./retriever-k9mmdcz5.js")).then(async(s)=>s.bm25Retriever(await this.loadIndex()))]),i=this.options.model??(async()=>{let s=await this.settings();if(!s)throw new L;let{loadModel:a}=await (G("11c9cspb"),import("./index-11c9cspb.js"));return a(s)}),t={retriever:n,model:i};if(this.options.instructions)t.instructions=this.options.instructions;if(this.options.lang)t.language=this.options.lang;return e(t)})()}async ask(e,n,i){let t=this.turns.filter((a)=>!a.error).map(({role:a,content:c})=>({role:a,content:c}));this.turns.push({role:"user",content:e});let s={role:"assistant",content:""};this.turns.push(s),this.persist();try{this.engine??=this.createEngine();let c=await(await this.engine.catch((p)=>{throw this.engine=null,p})).ask(e,{history:t,signal:i});s.passages=c.passages.map(({url:p,anchor:o,title:l,heading:d})=>({url:p,anchor:o,title:l,heading:d}));for await(let p of c.textStream)s.content+=p,n(s.content,s);s.cited=(await c.done).cited}catch(a){if(i.aborted)s.cited=[];else if(a instanceof L)throw this.turns.splice(-2,2),a;else s.error=await this.describe(a)}finally{this.persist()}return s}async describe(e){try{let{describeError:n}=await (G("11c9cspb"),import("./index-11c9cspb.js")),i=this.mode==="custom"?null:await this.settings(),t=n(e,i?.apiKey);return`${t.code}|${t.message}`}catch{return`unknown|${e instanceof Error?e.message:String(e)}`}}}class L extends Error{constructor(){super("murmur: no model set up")}}var W=`
|
|
6
6
|
:host {
|
|
7
7
|
--accent: var(--murmur-accent, #211c17);
|
|
8
8
|
--accent-ink: var(--murmur-accent-ink, #f6efe0);
|
|
@@ -201,4 +201,4 @@ footer a:hover { text-decoration: underline; }
|
|
|
201
201
|
@media (prefers-reduced-motion: reduce) {
|
|
202
202
|
*, .panel { animation: none !important; transition: none !important; }
|
|
203
203
|
}
|
|
204
|
-
`;var X={auth:"errorAuth","auth-or-network":"errorNetwork","rate-limit":"errorRate",overloaded:"errorRate"};class K extends HTMLElement{options={};session=null;t=P("en");root;launcher;panel;log;chatView;setupView;input;sendBtn;settingsBtn;controller=null;built=!1;unsubscribe=null;constructor(){super();this.root=this.attachShadow({mode:"open"})}configure(e){if(this.options={...this.options,...e},this.isConnected)this.build();return this}get isOpen(){return this.hasAttribute("inline")||this.launcher?.getAttribute("aria-expanded")==="true"}connectedCallback(){this.build()}disconnectedCallback(){this.controller?.abort(),this.unsubscribe?.()}async open(){if(!this.built)this.build();this.launcher.setAttribute("aria-expanded","true"),this.panel.hidden=!1,this.session?.persist(),sessionStorage.setItem(this.openKey,"1"),this.input.focus()}close(){if(this.hasAttribute("inline"))return;this.launcher.setAttribute("aria-expanded","false"),this.panel.hidden=!0,sessionStorage.removeItem(this.openKey),this.launcher.focus()}async ask(e){if(!this.isOpen)await this.open();this.input.value=e,await this.submit()}get openKey(){return`${this.options.storageKey??"murmur"}-open`}build(){let e=this.options,i=V(e.lang,document.documentElement.lang,navigator.language),n={...e.messages};if(this.t=P(i,n),e.position)this.setAttribute("position",e.position);if(e.theme&&e.theme!=="auto")this.setAttribute("theme",e.theme);if(e.accent)this.style.setProperty("--murmur-accent",e.accent);this.controller?.abort(),this.unsubscribe?.();let t=this.ownerDocument,s=t.createElement("style");s.textContent=U,this.launcher=r(t,"button",{class:"launcher",type:"button","aria-expanded":"false"}),this.launcher.setAttribute("aria-controls","murmur-panel"),this.launcher.append(r(t,"span",{class:"lamp","aria-hidden":"true"}),v(t,"span",this.t.open)),this.launcher.addEventListener("click",()=>void this.open()),this.panel=r(t,"section",{class:"panel",id:"murmur-panel",role:"dialog"}),this.panel.setAttribute("aria-label",this.t.title),this.panel.hidden=!this.hasAttribute("inline"),this.panel.addEventListener("keydown",(h)=>{if(h.key==="Escape")this.close()});let a=r(t,"header"),c=v(t,"h2",this.t.title),p=N(t,"reset",this.t.newChat);p.addEventListener("click",()=>{this.controller?.abort(),this.session?.clear(),this.renderChat(),this.input.focus()}),this.settingsBtn=N(t,"settings",this.t.settings),this.settingsBtn.hidden=!!e.model||!!e.llm?.endpoint,this.settingsBtn.addEventListener("click",()=>void this.showSetup());let o=N(t,"close",this.t.close);o.hidden=this.hasAttribute("inline"),o.addEventListener("click",()=>this.close()),a.append(c,p,this.settingsBtn,o),this.chatView=r(t,"div",{class:"chat"}),this.chatView.style.cssText="display:flex;flex-direction:column;flex:1;min-height:0",this.log=r(t,"div",{class:"log",role:"log","aria-live":"polite","aria-relevant":"additions"});let l=r(t,"form",{class:"composer"});this.input=r(t,"textarea",{rows:"1",placeholder:this.t.placeholder}),this.input.setAttribute("aria-label",this.t.placeholder),this.input.addEventListener("input",()=>this.autosize()),this.input.addEventListener("keydown",(h)=>{if(h.key==="Enter"&&!h.shiftKey&&!h.isComposing)h.preventDefault(),this.submit()}),this.sendBtn=r(t,"button",{class:"send",type:"submit"}),this.setSendMode("send"),l.addEventListener("submit",(h)=>{if(h.preventDefault(),this.controller)this.controller.abort();else this.submit()}),l.append(this.input,this.sendBtn);let d=r(t,"footer"),u=r(t,"a",{href:"https://maxgfr.github.io/murmur/",target:"_blank",rel:"noopener"});if(u.textContent=this.t.poweredBy,d.append(u),this.chatView.append(this.log,l,d),this.setupView=r(t,"div",{class:"setup"}),this.setupView.hidden=!0,this.panel.append(a,this.chatView,this.setupView),this.root.replaceChildren(s,this.launcher,this.panel),this.hasAttribute("inline"))this.launcher.hidden=!0;if(this.built=!0,this.session=new O({...e,lang:i}),this.renderChat(),e.open||!this.hasAttribute("inline")&&sessionStorage.getItem(this.openKey))this.open()}autosize(){this.input.style.height="auto",this.input.style.height=`${Math.min(this.input.scrollHeight,140)}px`}setSendMode(e){this.sendBtn.replaceChildren(R(this.ownerDocument,e)),this.sendBtn.setAttribute("aria-label",e==="send"?this.t.send:this.t.stop)}emit(e){this.dispatchEvent(new CustomEvent("murmur:state",{detail:e,bubbles:!0,composed:!0}))}renderChat(){let e=this.ownerDocument,i=this.session?.turns??[],n=[],t=r(e,"div",{class:"msg bot greeting"});t.append(v(e,"p",this.t.greeting)),n.push(t);for(let s of i)n.push(this.turnNode(s));this.log.replaceChildren(...n),this.log.scrollTop=this.log.scrollHeight}turnNode(e){let i=this.ownerDocument;if(e.role==="user"){let t=r(i,"div",{class:"msg user"});return t.textContent=e.content,t}let n=r(i,"div",{class:"msg bot"});return this.fillAnswer(n,e,!0),n}fillAnswer(e,i,n){let t=this.ownerDocument;if(i.error){let[o="unknown",...l]=i.error.split("|"),d=X[o],u=r(t,"div",{class:"error",role:"alert"});u.append(v(t,"p",d?this.t[d]:`${this.t.errorOther} ${l.join("|")}`));let h=r(t,"div",{class:"row"}),b=r(t,"button",{class:"text-btn",type:"button"});if(b.textContent=this.t.retry,b.addEventListener("click",()=>{let f=this.session?.turns??[],m=f.at(-2)?.content??"";if(f.splice(-2,2),this.renderChat(),m)this.ask(m)}),h.append(b),o==="auth"||o==="auth-or-network"){if(!this.settingsBtn.hidden){let f=r(t,"button",{class:"text-btn",type:"button"});f.textContent=this.t.settings,f.addEventListener("click",()=>void this.showSetup()),h.append(f)}}u.append(h),e.replaceChildren(u);return}let s=i.passages??[],a=r(t,"div");j(a,i.content,(o)=>{let l=s[o-1];if(!l)return null;let d=r(t,"a",{class:"cite",href:kt(l)});return d.textContent=String(o),d.setAttribute("aria-label",`${this.t.sources} ${o}: ${l.title}`),d});let c=[a];if(n&&!i.content.trim()&&!s.length){let o=r(t,"p",{class:"status"});o.append(r(t,"span",{class:"lamp off","aria-hidden":"true"}),t.createTextNode(this.t.notFound)),c.push(o)}let p=i.cited??[];if(n&&p.length){c.push(v(t,"p",this.t.sources,"sources-label"));let o=r(t,"ol",{class:"sources"});for(let l of p){let d=s[l-1];if(!d)continue;let u=r(t,"li"),h=r(t,"a",{href:kt(d)}),b=v(t,"span",String(l),"cite");b.setAttribute("aria-hidden","true");let f=r(t,"span",{class:"where"});if(f.append(t.createTextNode(d.title||d.url)),d.heading&&d.heading!==d.title)f.append(v(t,"span",` › ${d.heading}`));h.append(b,f),u.append(h),o.append(u)}c.push(o)}e.replaceChildren(...c)}async submit(){let e=this.input.value.trim();if(!e||this.controller||!this.session)return;if(await this.session.needsSetup()){await this.showSetup(e);return}let i=this.ownerDocument;this.input.value="",this.autosize(),this.log.append(this.turnNode({role:"user",content:e}));let n=r(i,"div",{class:"msg bot"}),t=r(i,"p",{class:"status"});t.append(r(i,"span",{class:"lamp busy","aria-hidden":"true"}),i.createTextNode(this.t.thinking)),n.append(t),this.log.append(n),this.log.scrollTop=this.log.scrollHeight,this.log.setAttribute("aria-busy","true");let s=new AbortController;this.controller=s,this.setSendMode("stop"),this.emit({state:"searching"});let a=!1;try{let c=await this.session.ask(e,(p,o)=>{if(!a)a=!0,this.emit({state:"streaming",passages:o.passages??[]});let l=this.log.scrollHeight-this.log.scrollTop-this.log.clientHeight<40;if(this.fillAnswer(n,o,!1),l)this.log.scrollTop=this.log.scrollHeight},s.signal);this.fillAnswer(n,c,!0),this.emit(c.error?{state:"error"}:{state:"done",passages:c.passages??[],cited:c.cited??[]})}catch(c){if(n.remove(),this.log.lastElementChild?.remove(),this.emit({state:"idle"}),c instanceof C)await this.showSetup(e);else throw c}finally{this.controller=null,this.setSendMode("send"),this.log.setAttribute("aria-busy","false"),this.log.scrollTop=this.log.scrollHeight}}async showSetup(e){if(!this.session)return;let i=this.ownerDocument,n=await this.session.store();await n.load(),n.open(),this.chatView.hidden=!0,this.setupView.hidden=!1;let t=this.t,s=r(i,"button",{class:"link-btn",type:"button"});s.textContent=t.back,s.addEventListener("click",()=>this.hideSetup());let a=v(i,"h3",t.setupTitle);a.style.cssText="margin:0;font-size:17px;font-weight:650";let c=v(i,"p",t.setupIntro,"intro"),p=W(i,t.local),o=L(i,t.provider,r(i,"select")),l=o.control,d=L(i,t.model,r(i,"select")),u=d.control,h=L(i,t.endpoint,r(i,"input",{type:"url",inputmode:"url"}),t.localHint),b=h.control,f=L(i,t.model,r(i,"input",{type:"text",autocomplete:"off"})),m=f.control,x=r(i,"div",{class:"presets"});for(let g of["ollama","lmstudio"]){let y=r(i,"button",{class:"text-btn",type:"button"});y.textContent=g==="ollama"?"Ollama":"LM Studio",y.addEventListener("click",async()=>{let{ENDPOINT_PRESETS:w}=await (G("e5entetm"),import("./index-e5entetm.js"));n.applyPreset(w[g])}),x.append(y)}let M=L(i,t.apiKey,r(i,"input",{type:"password",autocomplete:"off",spellcheck:"false"}),t.apiKeyHint),S=M.control,T=W(i,t.remember),E=r(i,"button",{class:"text-btn",type:"button"});E.textContent=t.test;let H=r(i,"p",{class:"result",role:"status"});H.style.margin="0";let A=r(i,"button",{class:"primary",type:"button"});A.textContent=t.save;let I=r(i,"button",{class:"link-btn",type:"button"});I.textContent=t.forget,p.input.addEventListener("change",()=>n.toggleCustom(p.input.checked)),l.addEventListener("change",()=>n.selectProvider(l.value)),u.addEventListener("change",()=>n.selectModel(u.value)),b.addEventListener("input",()=>n.setEndpoint(b.value)),m.addEventListener("input",()=>n.setCustomModel(m.value)),S.addEventListener("input",()=>n.setApiKey(S.value)),T.input.addEventListener("change",()=>n.setRememberKey(T.input.checked)),E.addEventListener("click",()=>void n.test()),A.addEventListener("click",async()=>{if(!(await n.save()).ok)return;if(this.hideSetup(),e)this.input.value=e,this.submit()}),I.addEventListener("click",()=>void n.clear());let z=r(i,"div",{class:"row"});z.append(E,H),this.setupView.replaceChildren(s,a,c,p.label,o.label,d.label,x,h.label,f.label,M.label,T.label,z,A,I);let Y=(g)=>{let y=g.draft.custom;if(p.input.checked=y,o.label.hidden=y,d.label.hidden=y,x.hidden=!y,h.label.hidden=!y,f.label.hidden=!y,g.catalogStatus==="ready")ee(i,l,g,t),te(i,u,g);else l.replaceChildren(B(i,"",g.catalogStatus==="error"?t.catalogError:t.loadingCatalog)),u.replaceChildren();if(b!==this.root.activeElement)b.value=g.draft.customEndpoint;if(m!==this.root.activeElement)m.value=g.draft.customModel;if(S!==this.root.activeElement)S.value=g.apiKey;T.input.checked=g.draft.rememberKey,E.disabled=!g.canTest||g.testing,E.textContent=g.testing?t.testing:t.test,A.disabled=!g.canSave;let w=g.testResult;H.className=w?w.ok?"result ok":"result fail":"result",H.textContent=!w?"":w.ok?t.testOk:`${t.testFail} ${w.reason}${w.message?` (${w.message})`:""}`};this.unsubscribe?.(),this.unsubscribe=n.subscribe(Y),(n.getState().draft.custom?b:l).focus()}hideSetup(){this.unsubscribe?.(),this.unsubscribe=null,this.setupView.hidden=!0,this.chatView.hidden=!1,this.input.focus()}}function ee(e,i,n,t){let s=[[t.recommended,n.providers.recommended],[t.otherProviders,n.providers.others]],a=`${n.draft.providerId}|${n.providers.recommended.length+n.providers.others.length}`;if(i.dataset.sig===a)return;i.dataset.sig=a,i.replaceChildren(...s.filter(([,c])=>c.length).map(([c,p])=>{let o=e.createElement("optgroup");return o.label=c,o.append(...p.map((l)=>B(e,l.id,l.name))),o})),i.value=n.draft.providerId}function te(e,i,n){let t=`${n.draft.providerId}|${n.draft.model}|${n.models.length}`;if(i.dataset.sig===t)return;i.dataset.sig=t;let s=n.recommendedModel?.id;i.replaceChildren(...n.models.map((a)=>B(e,a.id,a.id===s?`${a.name} ★`:a.name))),i.value=n.draft.model}function r(e,i,n={}){let t=e.createElement(i);for(let[s,a]of Object.entries(n))t.setAttribute(s,a);return t}function v(e,i,n,t){let s=e.createElement(i);if(s.textContent=n,t)s.className=t;return s}function B(e,i,n){let t=e.createElement("option");return t.value=i,t.textContent=n,t}function N(e,i,n){let t=r(e,"button",{class:"icon-btn",type:"button",title:n});return t.setAttribute("aria-label",n),t.append(R(e,i)),t}function L(e,i,n,t){let s=r(e,"label",{class:"field"});if(s.append(v(e,"span",i),n),t)s.append(v(e,"small",t));return{label:s,control:n}}function W(e,i){let n=r(e,"label",{class:"check"}),t=r(e,"input",{type:"checkbox"});return n.append(t,e.createTextNode(i)),{label:n,input:t}}function ne(e="murmur-chat"){if(!customElements.get(e))customElements.define(e,class extends K{});return customElements.get(e)}function _(e={}){ne();let{target:i,inline:n,...t}=e,s=document.createElement("murmur-chat");if(n)s.setAttribute("inline","");return s.configure(t),(i??document.body).append(s),s}function ie(e,i){let n={};if(e.index)n.index=new URL(e.index,i).href;let t={};if(e.provider)t.provider=e.provider;if(e.model)t.model=e.model;if(e.endpoint)t.endpoint=e.endpoint;if(Object.keys(t).length)n.llm=t;if(e.instructions)n.instructions=e.instructions;if(e.lang)n.lang=e.lang;let s={};if(e.title)s.title=e.title;if(e.greeting)s.greeting=e.greeting;if(e.placeholder)s.placeholder=e.placeholder;if(e.button)s.open=e.button;if(Object.keys(s).length)n.messages=s;if(e.position==="bottom-left"||e.position==="bottom-right")n.position=e.position;if(e.theme==="light"||e.theme==="dark"||e.theme==="auto")n.theme=e.theme;if(e.accent)n.accent=e.accent;if(e.catalog)n.catalogUrl=new URL(e.catalog,i).href;if(e.storageKey)n.storageKey=e.storageKey;if(e.open==="true")n.open=!0;return n}function q(){let e=import.meta.url,i=[...document.querySelectorAll("script[src]")].find((t)=>t.src===e&&!t.dataset.murmurReady);if(i)i.dataset.murmurReady="true";let n=ie(i?.dataset??{},location.href);n.catalogUrl??=new URL("./models.json",e).href,_(n)}if(typeof document<"u"&&!document.querySelector("murmur-chat:not([inline])"))if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",q,{once:!0});else q();export{ie as optionsFromDataset};
|
|
204
|
+
`;var ee={auth:"errorAuth","auth-or-network":"errorNetwork","rate-limit":"errorRate",overloaded:"errorRate"},te=typeof HTMLElement>"u"?class{}:HTMLElement;class K extends te{options={};session=null;t=P("en");root;launcher;panel;log;chatView;setupView;input;sendBtn;settingsBtn;controller=null;built=!1;unsubscribe=null;constructor(){super();this.root=this.attachShadow({mode:"open"})}configure(e){if(this.options={...this.options,...e},this.isConnected)this.build();return this}get isOpen(){return this.hasAttribute("inline")||this.launcher?.getAttribute("aria-expanded")==="true"}connectedCallback(){this.build()}disconnectedCallback(){this.controller?.abort(),this.unsubscribe?.()}async open(){if(!this.built)this.build();this.launcher.setAttribute("aria-expanded","true"),this.panel.hidden=!1,this.session?.persist(),N("set",this.openKey),this.input.focus()}close(){if(this.hasAttribute("inline"))return;this.launcher.setAttribute("aria-expanded","false"),this.panel.hidden=!0,N("remove",this.openKey),this.launcher.focus()}async ask(e){if(!this.isOpen)await this.open();this.input.value=e,await this.submit()}get openKey(){return`${this.options.storageKey??"murmur"}-open`}build(){let e=this.options,n=j(e.lang,document.documentElement.lang,navigator.language),i={...e.messages};if(this.t=P(n,i),e.position)this.setAttribute("position",e.position);if(e.theme&&e.theme!=="auto")this.setAttribute("theme",e.theme);if(e.accent)this.style.setProperty("--murmur-accent",e.accent);this.controller?.abort(),this.unsubscribe?.();let t=this.ownerDocument,s=t.createElement("style");s.textContent=W,this.launcher=r(t,"button",{class:"launcher",type:"button","aria-expanded":"false"}),this.launcher.setAttribute("aria-controls","murmur-panel"),this.launcher.append(r(t,"span",{class:"lamp","aria-hidden":"true"}),v(t,"span",this.t.open)),this.launcher.addEventListener("click",()=>void this.open()),this.panel=r(t,"section",{class:"panel",id:"murmur-panel",role:"dialog"}),this.panel.setAttribute("aria-label",this.t.title),this.panel.hidden=!this.hasAttribute("inline"),this.panel.addEventListener("keydown",(h)=>{if(h.key==="Escape")this.close()});let a=r(t,"header"),c=v(t,"h2",this.t.title),p=B(t,"reset",this.t.newChat);p.addEventListener("click",()=>{this.controller?.abort(),this.session?.clear(),this.renderChat(),this.input.focus()}),this.settingsBtn=B(t,"settings",this.t.settings),this.settingsBtn.hidden=!!e.model||!!e.llm?.endpoint,this.settingsBtn.addEventListener("click",()=>void this.showSetup());let o=B(t,"close",this.t.close);o.hidden=this.hasAttribute("inline"),o.addEventListener("click",()=>this.close()),a.append(c,p,this.settingsBtn,o),this.chatView=r(t,"div",{class:"chat"}),this.chatView.style.cssText="display:flex;flex-direction:column;flex:1;min-height:0",this.log=r(t,"div",{class:"log",role:"log","aria-live":"polite","aria-relevant":"additions"});let l=r(t,"form",{class:"composer"});this.input=r(t,"textarea",{rows:"1",placeholder:this.t.placeholder}),this.input.setAttribute("aria-label",this.t.placeholder),this.input.addEventListener("input",()=>this.autosize()),this.input.addEventListener("keydown",(h)=>{if(h.key==="Enter"&&!h.shiftKey&&!h.isComposing)h.preventDefault(),this.submit()}),this.sendBtn=r(t,"button",{class:"send",type:"submit"}),this.setSendMode("send"),l.addEventListener("submit",(h)=>{if(h.preventDefault(),this.controller)this.controller.abort();else this.submit()}),l.append(this.input,this.sendBtn);let d=r(t,"footer"),u=r(t,"a",{href:"https://maxgfr.github.io/murmur/",target:"_blank",rel:"noopener"});if(u.textContent=this.t.poweredBy,d.append(u),this.chatView.append(this.log,l,d),this.setupView=r(t,"div",{class:"setup"}),this.setupView.hidden=!0,this.panel.append(a,this.chatView,this.setupView),this.root.replaceChildren(s,this.launcher,this.panel),this.hasAttribute("inline"))this.launcher.hidden=!0;if(this.built=!0,this.session=new O({...e,lang:n}),this.renderChat(),e.open||!this.hasAttribute("inline")&&N("get",this.openKey))this.open()}autosize(){this.input.style.height="auto",this.input.style.height=`${Math.min(this.input.scrollHeight,140)}px`}setSendMode(e){this.sendBtn.replaceChildren(R(this.ownerDocument,e)),this.sendBtn.setAttribute("aria-label",e==="send"?this.t.send:this.t.stop)}emit(e){this.dispatchEvent(new CustomEvent("murmur:state",{detail:e,bubbles:!0,composed:!0}))}renderChat(){let e=this.ownerDocument,n=this.session?.turns??[],i=[],t=r(e,"div",{class:"msg bot greeting"});t.append(v(e,"p",this.t.greeting)),i.push(t);for(let s of n)i.push(this.turnNode(s));this.log.replaceChildren(...i),this.log.scrollTop=this.log.scrollHeight}turnNode(e){let n=this.ownerDocument;if(e.role==="user"){let t=r(n,"div",{class:"msg user"});return t.textContent=e.content,t}let i=r(n,"div",{class:"msg bot"});return this.fillAnswer(i,e,!0),i}fillAnswer(e,n,i){let t=this.ownerDocument;if(n.error){let[o="unknown",...l]=n.error.split("|"),d=ee[o],u=r(t,"div",{class:"error",role:"alert"});u.append(v(t,"p",d?this.t[d]:`${this.t.errorOther} ${l.join("|")}`));let h=r(t,"div",{class:"row"}),b=r(t,"button",{class:"text-btn",type:"button"});if(b.textContent=this.t.retry,b.addEventListener("click",()=>{let f=this.session?.turns??[],m=f.at(-2)?.content??"";if(f.splice(-2,2),this.renderChat(),m)this.ask(m)}),h.append(b),o==="auth"||o==="auth-or-network"){if(!this.settingsBtn.hidden){let f=r(t,"button",{class:"text-btn",type:"button"});f.textContent=this.t.settings,f.addEventListener("click",()=>void this.showSetup()),h.append(f)}}u.append(h),e.replaceChildren(u);return}let s=n.passages??[],a=r(t,"div");U(a,n.content,(o)=>{let l=s[o-1];if(!l)return null;let d=r(t,"a",{class:"cite",href:kt(l)});return d.textContent=String(o),d.setAttribute("aria-label",`${this.t.sources} ${o}: ${l.title}`),d});let c=[a];if(i&&!n.content.trim()&&!s.length){let o=r(t,"p",{class:"status"});o.append(r(t,"span",{class:"lamp off","aria-hidden":"true"}),t.createTextNode(this.t.notFound)),c.push(o)}let p=n.cited??[];if(i&&p.length){c.push(v(t,"p",this.t.sources,"sources-label"));let o=r(t,"ol",{class:"sources"});for(let l of p){let d=s[l-1];if(!d)continue;let u=r(t,"li"),h=r(t,"a",{href:kt(d)}),b=v(t,"span",String(l),"cite");b.setAttribute("aria-hidden","true");let f=r(t,"span",{class:"where"});if(f.append(t.createTextNode(d.title||d.url)),d.heading&&d.heading!==d.title)f.append(v(t,"span",` › ${d.heading}`));h.append(b,f),u.append(h),o.append(u)}c.push(o)}e.replaceChildren(...c)}async submit(){let e=this.input.value.trim();if(!e||this.controller||!this.session)return;if(await this.session.needsSetup()){await this.showSetup(e);return}let n=this.ownerDocument;this.input.value="",this.autosize(),this.log.append(this.turnNode({role:"user",content:e}));let i=r(n,"div",{class:"msg bot"}),t=r(n,"p",{class:"status"});t.append(r(n,"span",{class:"lamp busy","aria-hidden":"true"}),n.createTextNode(this.t.thinking)),i.append(t),this.log.append(i),this.log.scrollTop=this.log.scrollHeight,this.log.setAttribute("aria-busy","true");let s=new AbortController;this.controller=s,this.setSendMode("stop"),this.emit({state:"searching"});let a=!1;try{let c=await this.session.ask(e,(p,o)=>{if(!a)a=!0,this.emit({state:"streaming",passages:o.passages??[]});let l=this.log.scrollHeight-this.log.scrollTop-this.log.clientHeight<40;if(this.fillAnswer(i,o,!1),l)this.log.scrollTop=this.log.scrollHeight},s.signal);this.fillAnswer(i,c,!0),this.emit(c.error?{state:"error"}:{state:"done",passages:c.passages??[],cited:c.cited??[]})}catch(c){if(i.remove(),this.log.lastElementChild?.remove(),this.emit({state:"idle"}),c instanceof L)await this.showSetup(e);else throw c}finally{this.controller=null,this.setSendMode("send"),this.log.setAttribute("aria-busy","false"),this.log.scrollTop=this.log.scrollHeight}}async showSetup(e){if(!this.session)return;let n=this.ownerDocument,i=await this.session.store();await i.load(),i.open(),this.chatView.hidden=!0,this.setupView.hidden=!1;let t=this.t,s=r(n,"button",{class:"link-btn",type:"button"});s.textContent=t.back,s.addEventListener("click",()=>this.hideSetup());let a=v(n,"h3",t.setupTitle);a.style.cssText="margin:0;font-size:17px;font-weight:650";let c=v(n,"p",t.setupIntro,"intro"),p=_(n,t.local),o=C(n,t.provider,r(n,"select")),l=o.control,d=C(n,t.model,r(n,"select")),u=d.control,h=C(n,t.endpoint,r(n,"input",{type:"url",inputmode:"url"}),t.localHint),b=h.control,f=C(n,t.model,r(n,"input",{type:"text",autocomplete:"off"})),m=f.control,x=r(n,"div",{class:"presets"});for(let g of["ollama","lmstudio"]){let y=r(n,"button",{class:"text-btn",type:"button"});y.textContent=g==="ollama"?"Ollama":"LM Studio",y.addEventListener("click",async()=>{let{ENDPOINT_PRESETS:w}=await (G("e5entetm"),import("./index-e5entetm.js"));i.applyPreset(w[g])}),x.append(y)}let M=C(n,t.apiKey,r(n,"input",{type:"password",autocomplete:"off",spellcheck:"false"}),t.apiKeyHint),S=M.control,T=_(n,t.remember),E=r(n,"button",{class:"text-btn",type:"button"});E.textContent=t.test;let H=r(n,"p",{class:"result",role:"status"});H.style.margin="0";let A=r(n,"button",{class:"primary",type:"button"});A.textContent=t.save;let I=r(n,"button",{class:"link-btn",type:"button"});I.textContent=t.forget,p.input.addEventListener("change",()=>i.toggleCustom(p.input.checked)),l.addEventListener("change",()=>i.selectProvider(l.value)),u.addEventListener("change",()=>i.selectModel(u.value)),b.addEventListener("input",()=>i.setEndpoint(b.value)),m.addEventListener("input",()=>i.setCustomModel(m.value)),S.addEventListener("input",()=>i.setApiKey(S.value)),T.input.addEventListener("change",()=>i.setRememberKey(T.input.checked)),E.addEventListener("click",()=>void i.test()),A.addEventListener("click",async()=>{if(!(await i.save()).ok)return;if(this.hideSetup(),e)this.input.value=e,this.submit()}),I.addEventListener("click",()=>void i.clear());let D=r(n,"div",{class:"row"});D.append(E,H),this.setupView.replaceChildren(s,a,c,p.label,o.label,d.label,x,h.label,f.label,M.label,T.label,D,A,I);let J=(g)=>{let y=g.draft.custom;if(p.input.checked=y,o.label.hidden=y,d.label.hidden=y,x.hidden=!y,h.label.hidden=!y,f.label.hidden=!y,g.catalogStatus==="ready")ne(n,l,g,t),ie(n,u,g);else l.replaceChildren(z(n,"",g.catalogStatus==="error"?t.catalogError:t.loadingCatalog)),u.replaceChildren();if(b!==this.root.activeElement)b.value=g.draft.customEndpoint;if(m!==this.root.activeElement)m.value=g.draft.customModel;if(S!==this.root.activeElement)S.value=g.apiKey;T.input.checked=g.draft.rememberKey,E.disabled=!g.canTest||g.testing,E.textContent=g.testing?t.testing:t.test,A.disabled=!g.canSave;let w=g.testResult;H.className=w?w.ok?"result ok":"result fail":"result",H.textContent=!w?"":w.ok?t.testOk:`${t.testFail} ${w.reason}${w.message?` (${w.message})`:""}`};this.unsubscribe?.(),this.unsubscribe=i.subscribe(J),(i.getState().draft.custom?b:l).focus()}hideSetup(){this.unsubscribe?.(),this.unsubscribe=null,this.setupView.hidden=!0,this.chatView.hidden=!1,this.input.focus()}}function ne(e,n,i,t){let s=[[t.recommended,i.providers.recommended],[t.otherProviders,i.providers.others]],a=`${i.draft.providerId}|${i.providers.recommended.length+i.providers.others.length}`;if(n.dataset.sig===a)return;n.dataset.sig=a,n.replaceChildren(...s.filter(([,c])=>c.length).map(([c,p])=>{let o=e.createElement("optgroup");return o.label=c,o.append(...p.map((l)=>z(e,l.id,l.name))),o})),n.value=i.draft.providerId}function ie(e,n,i){let t=`${i.draft.providerId}|${i.draft.model}|${i.models.length}`;if(n.dataset.sig===t)return;n.dataset.sig=t;let s=i.recommendedModel?.id;n.replaceChildren(...i.models.map((a)=>z(e,a.id,a.id===s?`${a.name} ★`:a.name))),n.value=i.draft.model}function N(e,n){try{if(e==="set")sessionStorage.setItem(n,"1");else if(e==="remove")sessionStorage.removeItem(n);else return sessionStorage.getItem(n)!==null}catch{}return!1}function r(e,n,i={}){let t=e.createElement(n);for(let[s,a]of Object.entries(i))t.setAttribute(s,a);return t}function v(e,n,i,t){let s=e.createElement(n);if(s.textContent=i,t)s.className=t;return s}function z(e,n,i){let t=e.createElement("option");return t.value=n,t.textContent=i,t}function B(e,n,i){let t=r(e,"button",{class:"icon-btn",type:"button",title:i});return t.setAttribute("aria-label",i),t.append(R(e,n)),t}function C(e,n,i,t){let s=r(e,"label",{class:"field"});if(s.append(v(e,"span",n),i),t)s.append(v(e,"small",t));return{label:s,control:i}}function _(e,n){let i=r(e,"label",{class:"check"}),t=r(e,"input",{type:"checkbox"});return i.append(t,e.createTextNode(n)),{label:i,input:t}}function se(e="murmur-chat"){if(!customElements.get(e))customElements.define(e,class extends K{});return customElements.get(e)}function q(e={}){se();let{target:n,inline:i,...t}=e,s=document.createElement("murmur-chat");if(i)s.setAttribute("inline","");return s.configure(t),(n??document.body).append(s),s}function re(e,n){let i={};if(e.index)i.index=new URL(e.index,n).href;let t={};if(e.provider)t.provider=e.provider;if(e.model)t.model=e.model;if(e.endpoint)t.endpoint=e.endpoint;if(Object.keys(t).length)i.llm=t;if(e.instructions)i.instructions=e.instructions;if(e.lang)i.lang=e.lang;let s={};if(e.title)s.title=e.title;if(e.greeting)s.greeting=e.greeting;if(e.placeholder)s.placeholder=e.placeholder;if(e.button)s.open=e.button;if(Object.keys(s).length)i.messages=s;if(e.position==="bottom-left"||e.position==="bottom-right")i.position=e.position;if(e.theme==="light"||e.theme==="dark"||e.theme==="auto")i.theme=e.theme;if(e.accent)i.accent=e.accent;if(e.catalog)i.catalogUrl=new URL(e.catalog,n).href;if(e.storageKey)i.storageKey=e.storageKey;if(e.open==="true")i.open=!0;return i}function Y(){let e=import.meta.url,n=[...document.querySelectorAll("script[src]")].find((t)=>t.src===e&&!t.dataset.murmurReady);if(n)n.dataset.murmurReady="true";let i=re(n?.dataset??{},location.href);i.catalogUrl??=new URL("./models.json",e).href,q(i)}if(typeof document<"u"&&!document.querySelector("murmur-chat:not([inline])"))if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",Y,{once:!0});else Y();export{re as optionsFromDataset};
|
|
@@ -223,6 +223,7 @@ function renderMarkdown2(target, markdown, cite) {
|
|
|
223
223
|
for (i++;i < lines.length && !/^\s*```/.test(lines[i] ?? ""); i++)
|
|
224
224
|
body.push(lines[i] ?? "");
|
|
225
225
|
const pre = doc.createElement("pre");
|
|
226
|
+
pre.tabIndex = 0;
|
|
226
227
|
const code = doc.createElement("code");
|
|
227
228
|
code.textContent = body.join(`
|
|
228
229
|
`);
|
|
@@ -369,6 +370,7 @@ class ChatSession {
|
|
|
369
370
|
this.turns.push({ role: "user", content: question });
|
|
370
371
|
const turn = { role: "assistant", content: "" };
|
|
371
372
|
this.turns.push(turn);
|
|
373
|
+
this.persist();
|
|
372
374
|
try {
|
|
373
375
|
this.engine ??= this.createEngine();
|
|
374
376
|
const engine = await this.engine.catch((error) => {
|
|
@@ -628,8 +630,10 @@ var ERROR_KEYS = {
|
|
|
628
630
|
"rate-limit": "errorRate",
|
|
629
631
|
overloaded: "errorRate"
|
|
630
632
|
};
|
|
633
|
+
var Base = typeof HTMLElement === "undefined" ? class {
|
|
634
|
+
} : HTMLElement;
|
|
631
635
|
|
|
632
|
-
class MurmurChat2 extends
|
|
636
|
+
class MurmurChat2 extends Base {
|
|
633
637
|
options = {};
|
|
634
638
|
session = null;
|
|
635
639
|
t = messagesFor2("en");
|
|
@@ -671,7 +675,7 @@ class MurmurChat2 extends HTMLElement {
|
|
|
671
675
|
this.launcher.setAttribute("aria-expanded", "true");
|
|
672
676
|
this.panel.hidden = false;
|
|
673
677
|
this.session?.persist();
|
|
674
|
-
|
|
678
|
+
tabStorage("set", this.openKey);
|
|
675
679
|
this.input.focus();
|
|
676
680
|
}
|
|
677
681
|
close() {
|
|
@@ -679,7 +683,7 @@ class MurmurChat2 extends HTMLElement {
|
|
|
679
683
|
return;
|
|
680
684
|
this.launcher.setAttribute("aria-expanded", "false");
|
|
681
685
|
this.panel.hidden = true;
|
|
682
|
-
|
|
686
|
+
tabStorage("remove", this.openKey);
|
|
683
687
|
this.launcher.focus();
|
|
684
688
|
}
|
|
685
689
|
async ask(question) {
|
|
@@ -787,7 +791,7 @@ class MurmurChat2 extends HTMLElement {
|
|
|
787
791
|
this.built = true;
|
|
788
792
|
this.session = new ChatSession({ ...o, lang });
|
|
789
793
|
this.renderChat();
|
|
790
|
-
if (o.open || !this.hasAttribute("inline") &&
|
|
794
|
+
if (o.open || !this.hasAttribute("inline") && tabStorage("get", this.openKey))
|
|
791
795
|
this.open();
|
|
792
796
|
}
|
|
793
797
|
autosize() {
|
|
@@ -1087,6 +1091,17 @@ function fillModels(doc, select, s) {
|
|
|
1087
1091
|
select.replaceChildren(...s.models.map((m) => option(doc, m.id, m.id === rec ? `${m.name} ★` : m.name)));
|
|
1088
1092
|
select.value = s.draft.model;
|
|
1089
1093
|
}
|
|
1094
|
+
function tabStorage(op, key) {
|
|
1095
|
+
try {
|
|
1096
|
+
if (op === "set")
|
|
1097
|
+
sessionStorage.setItem(key, "1");
|
|
1098
|
+
else if (op === "remove")
|
|
1099
|
+
sessionStorage.removeItem(key);
|
|
1100
|
+
else
|
|
1101
|
+
return sessionStorage.getItem(key) !== null;
|
|
1102
|
+
} catch {}
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1090
1105
|
function el(doc, tag, attrs = {}) {
|
|
1091
1106
|
const node = doc.createElement(tag);
|
|
1092
1107
|
for (const [k, v] of Object.entries(attrs))
|
package/dist/lib/embed.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__chunks,
|
|
3
3
|
mountMurmur2
|
|
4
|
-
} from "./chunks/index-
|
|
5
|
-
__chunks(import.meta.url,["
|
|
4
|
+
} from "./chunks/index-0x7gav29.js";
|
|
5
|
+
__chunks(import.meta.url,["9w5px1fq","0x7gav29","1rw4z2n6","hp2p9tp9","vb9smwta","kaeec41p","wq1d15n7","6re28wc1","z8pn9sdj","1zk822m5","fs6gmhz9"],[["./embed.js",1],["./chunks/index-0x7gav29.js",7],["./chunks/retriever-1rw4z2n6.js",8],["./indexer/index.js",9],["./llm/index.js"],["./chunks/chunk-kaeec41p.js",9],["./chunks/murmur-wq1d15n7.js",10,8],["./chunks/index-6re28wc1.js"],["./chunks/index-z8pn9sdj.js",7],["./chunks/index-1zk822m5.js"],["./chunks/index-fs6gmhz9.js",8,7]],0);
|
|
6
6
|
|
|
7
7
|
// src/embed.ts
|
|
8
8
|
function optionsFromDataset(data, base) {
|
package/dist/lib/widget/index.js
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
MurmurChat2,
|
|
9
9
|
defineMurmurChat2,
|
|
10
10
|
mountMurmur2
|
|
11
|
-
} from "../chunks/index-
|
|
12
|
-
__chunks(import.meta.url,["
|
|
11
|
+
} from "../chunks/index-0x7gav29.js";
|
|
12
|
+
__chunks(import.meta.url,["988ctnns","0x7gav29","1rw4z2n6","hp2p9tp9","vb9smwta","kaeec41p","wq1d15n7","6re28wc1","z8pn9sdj","1zk822m5","fs6gmhz9"],[["./index.js",1],["../chunks/index-0x7gav29.js",7],["../chunks/retriever-1rw4z2n6.js",8],["../indexer/index.js",9],["../llm/index.js"],["../chunks/chunk-kaeec41p.js",9],["../chunks/murmur-wq1d15n7.js",10,8],["../chunks/index-6re28wc1.js"],["../chunks/index-z8pn9sdj.js",7],["../chunks/index-1zk822m5.js"],["../chunks/index-fs6gmhz9.js",8,7]],0);
|
|
13
13
|
export {
|
|
14
14
|
MESSAGES2 as MESSAGES,
|
|
15
15
|
MurmurChat2 as MurmurChat,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type LanguageModel } from "ai";
|
|
2
|
-
import { type Retriever } from "../rag/retriever";
|
|
3
|
-
import type { MurmurIndex, Passage } from "../rag/types";
|
|
2
|
+
import { type Retriever } from "../rag/retriever.js";
|
|
3
|
+
import type { MurmurIndex, Passage } from "../rag/types.js";
|
|
4
4
|
export interface MurmurOptions {
|
|
5
5
|
/** The index to answer from; ignored when `retriever` is given. */
|
|
6
6
|
index?: MurmurIndex;
|
package/dist/types/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WidgetOptions } from "./widget/index";
|
|
1
|
+
import type { WidgetOptions } from "./widget/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* The one-line embed: reads the options from the script tag's data attributes and adds the
|
|
4
4
|
* widget. `<script type="module" src=".../murmur.js" data-index="/murmur-index.json"></script>`
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { type Answer, createMurmur, type Murmur, type MurmurOptions, searchQuery, type Turn, } from "./core/murmur";
|
|
2
|
-
export * from "./rag/index";
|
|
1
|
+
export { type Answer, createMurmur, type Murmur, type MurmurOptions, searchQuery, type Turn, } from "./core/murmur.js";
|
|
2
|
+
export * from "./rag/index.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Section } from "../rag/chunk";
|
|
1
|
+
import { type Section } from "../rag/chunk.js";
|
|
2
2
|
/** Elements never worth indexing: navigation, chrome, scripts, and murmur itself. */
|
|
3
3
|
export declare const DEFAULT_EXCLUDE = "script,style,noscript,template,svg,canvas,iframe,nav,header,footer,aside,form,button,dialog,[hidden],[aria-hidden='true'],[data-murmur-ignore],murmur-chat";
|
|
4
4
|
/** Where the content of a page is looked for, first match wins. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PageInput } from "./index";
|
|
1
|
+
import type { PageInput } from "./index.js";
|
|
2
2
|
/** URL of a file under the site root: `docs/index.html` → `/docs/`. */
|
|
3
3
|
export declare function urlOf(file: string, rootDir: string, base: string): string;
|
|
4
4
|
export declare function pageOf(url: string, body: string, kind: string): PageInput;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Section } from "../rag/chunk";
|
|
2
|
-
import type { MurmurIndex } from "../rag/types";
|
|
3
|
-
import { type ExtractOptions } from "./extract";
|
|
4
|
-
export { DEFAULT_EXCLUDE, DEFAULT_ROOT, type ExtractOptions, extractSections } from "./extract";
|
|
1
|
+
import { type Section } from "../rag/chunk.js";
|
|
2
|
+
import type { MurmurIndex } from "../rag/types.js";
|
|
3
|
+
import { type ExtractOptions } from "./extract.js";
|
|
4
|
+
export { DEFAULT_EXCLUDE, DEFAULT_ROOT, type ExtractOptions, extractSections } from "./extract.js";
|
|
5
5
|
/** One page to index: give its HTML, its Markdown, or plain text. */
|
|
6
6
|
export type PageInput = {
|
|
7
7
|
url: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Chunk, MurmurIndex } from "./types";
|
|
1
|
+
import type { Chunk, MurmurIndex } from "./types.js";
|
|
2
2
|
/** Longest passage, in characters (about 500 tokens). */
|
|
3
3
|
export declare const MAX_CHUNK = 2000;
|
|
4
4
|
/** A titled block of text, as an extractor reads it from a page. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Passage } from "./types";
|
|
1
|
+
import type { Passage } from "./types.js";
|
|
2
2
|
/** Rough token estimate (about 4 characters per token), without a tokenizer. */
|
|
3
3
|
export declare function estimateTokens(text: string): number;
|
|
4
4
|
/** Passages between delimiters, as data, numbered from 1; delimiter look-alikes inside are neutralized. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildIndex, chunkSections, isMurmurIndex, MAX_CHUNK, markdownSections, type Section, slugify, splitText, } from "./chunk";
|
|
2
|
-
export { citedSources, estimateTokens, formatSources, groundingRules, passageHref, SOURCES_RULE, } from "./ground";
|
|
3
|
-
export { bm25Retriever, passagesOf, type Retriever, type SearchOptions } from "./retriever";
|
|
4
|
-
export type { Chunk, IndexDoc, MurmurIndex, Passage } from "./types";
|
|
1
|
+
export { buildIndex, chunkSections, isMurmurIndex, MAX_CHUNK, markdownSections, type Section, slugify, splitText, } from "./chunk.js";
|
|
2
|
+
export { citedSources, estimateTokens, formatSources, groundingRules, passageHref, SOURCES_RULE, } from "./ground.js";
|
|
3
|
+
export { bm25Retriever, passagesOf, type Retriever, type SearchOptions } from "./retriever.js";
|
|
4
|
+
export type { Chunk, IndexDoc, MurmurIndex, Passage } from "./types.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MurmurIndex, Passage } from "./types";
|
|
1
|
+
import type { MurmurIndex, Passage } from "./types.js";
|
|
2
2
|
/** Finds the passages that answer a question. Bring your own (embeddings, a search API) to replace BM25. */
|
|
3
3
|
export interface Retriever {
|
|
4
4
|
search(query: string, options?: SearchOptions): Promise<Passage[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { LlmSettingsStore } from "../llm/index";
|
|
2
|
-
import { type ChatTurn, type WidgetOptions } from "./session";
|
|
1
|
+
import type { LlmSettingsStore } from "../llm/index.js";
|
|
2
|
+
import { type ChatTurn, type WidgetOptions } from "./session.js";
|
|
3
3
|
/** What `murmur:state` events carry, for pages that want to show what the widget is doing. */
|
|
4
4
|
export interface MurmurStateDetail {
|
|
5
5
|
state: "searching" | "streaming" | "done" | "error" | "idle";
|
|
@@ -11,7 +11,8 @@ export interface MurmurStateDetail {
|
|
|
11
11
|
* `element.configure(options)` before or after it is connected; attributes `inline`,
|
|
12
12
|
* `position` and `theme` style it.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
declare const Base: typeof HTMLElement;
|
|
15
|
+
export declare class MurmurChat extends Base {
|
|
15
16
|
private options;
|
|
16
17
|
private session;
|
|
17
18
|
private t;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { MurmurChat } from "./element";
|
|
2
|
-
import type { WidgetOptions } from "./session";
|
|
3
|
-
export { MurmurChat, type MurmurStateDetail } from "./element";
|
|
4
|
-
export { MESSAGES, type Messages, messagesFor, pickLanguage } from "./i18n";
|
|
5
|
-
export { type CitationRenderer, renderMarkdown } from "./markdown";
|
|
6
|
-
export { type ChatTurn, SetupNeeded, type WidgetOptions } from "./session";
|
|
1
|
+
import { MurmurChat } from "./element.js";
|
|
2
|
+
import type { WidgetOptions } from "./session.js";
|
|
3
|
+
export { MurmurChat, type MurmurStateDetail } from "./element.js";
|
|
4
|
+
export { MESSAGES, type Messages, messagesFor, pickLanguage } from "./i18n.js";
|
|
5
|
+
export { type CitationRenderer, renderMarkdown } from "./markdown.js";
|
|
6
|
+
export { type ChatTurn, SetupNeeded, type WidgetOptions } from "./session.js";
|
|
7
7
|
/** Registers `<murmur-chat>` (or another tag name) once. */
|
|
8
8
|
export declare function defineMurmurChat(tag?: string): typeof MurmurChat;
|
|
9
9
|
/** Adds a configured widget to the page (the floating bubble, or `inline` inside `target`). */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { LanguageModel } from "ai";
|
|
2
|
-
import type { Turn } from "../core/murmur";
|
|
3
|
-
import type { LlmConfig, LlmSettings, LlmSettingsStore } from "../llm/index";
|
|
4
|
-
import type { Retriever } from "../rag/retriever";
|
|
5
|
-
import type { MurmurIndex, Passage } from "../rag/types";
|
|
6
|
-
import type { Messages } from "./i18n";
|
|
2
|
+
import type { Turn } from "../core/murmur.js";
|
|
3
|
+
import type { LlmConfig, LlmSettings, LlmSettingsStore } from "../llm/index.js";
|
|
4
|
+
import type { Retriever } from "../rag/retriever.js";
|
|
5
|
+
import type { MurmurIndex, Passage } from "../rag/types.js";
|
|
6
|
+
import type { Messages } from "./i18n.js";
|
|
7
7
|
export interface WidgetOptions {
|
|
8
8
|
/** URL of a `murmur index` JSON, or the index itself. Default: the page the widget is on. */
|
|
9
9
|
index?: string | MurmurIndex;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "murmur-rag",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Embeddable AI chat for static sites: one script tag, a mini RAG over your own pages, any LLM through llm-connect (your proxy or the visitor's own key). No backend.",
|
|
5
5
|
"author": "maxgfr",
|
|
6
6
|
"license": "MIT",
|
|
@@ -90,7 +90,8 @@
|
|
|
90
90
|
"test": "bun test tests",
|
|
91
91
|
"test:e2e": "bun run build && playwright test",
|
|
92
92
|
"catalog": "llm-models catalog --npm callable --require openai,anthropic,google,mistral --out assets/models.json",
|
|
93
|
-
"release": "semantic-release"
|
|
93
|
+
"release": "semantic-release",
|
|
94
|
+
"test:package": "bun run build:lib && bun scripts/check-package.ts"
|
|
94
95
|
},
|
|
95
96
|
"dependencies": {
|
|
96
97
|
"@ai-sdk/anthropic": "^4.0.90",
|
|
@@ -105,6 +106,7 @@
|
|
|
105
106
|
"minisearch": "^7.2.0"
|
|
106
107
|
},
|
|
107
108
|
"devDependencies": {
|
|
109
|
+
"@axe-core/playwright": "^4.14.0",
|
|
108
110
|
"@biomejs/biome": "^2.6.3",
|
|
109
111
|
"@playwright/test": "^1.64.1",
|
|
110
112
|
"@semantic-release/exec": "^7.1.0",
|