hearback-widget 0.1.0 → 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.
Files changed (2) hide show
  1. package/dist/widget.js +19 -14
  2. package/package.json +1 -1
package/dist/widget.js CHANGED
@@ -1,7 +1,7 @@
1
- (()=>{var N=Object.create;var y=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var D=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var j=(n,s,e)=>s in n?y(n,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[s]=e;var P=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(s,e)=>(typeof require<"u"?require:s)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});var O=(n,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of H(s))!A.call(n,r)&&r!==e&&y(n,r,{get:()=>s[r],enumerable:!(t=B(s,r))||t.enumerable});return n};var q=(n,s,e)=>(e=n!=null?N(D(n)):{},O(s||!n||!n.__esModule?y(e,"default",{value:n,enumerable:!0}):e,n));var p=(n,s,e)=>j(n,typeof s!="symbol"?s+"":s,e);var C=`
1
+ (()=>{var R=Object.create;var w=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var j=(n,r,e)=>r in n?w(n,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[r]=e;var $=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(r,e)=>(typeof require<"u"?require:r)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});var P=(n,r,e,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of A(r))!D.call(n,s)&&s!==e&&w(n,s,{get:()=>r[s],enumerable:!(t=N(r,s))||t.enumerable});return n};var O=(n,r,e)=>(e=n!=null?R(H(n)):{},P(r||!n||!n.__esModule?w(e,"default",{value:n,enumerable:!0}):e,n));var p=(n,r,e)=>j(n,typeof r!="symbol"?r+"":r,e);var C=`
2
2
  :host {
3
- --hb-accent: #F59E0B;
4
- --hb-accent-hover: #D97706;
3
+ --hb-accent: #5B5FE8;
4
+ --hb-accent-hover: #4B4FD4;
5
5
  --hb-bg: #FFFFFF;
6
6
  --hb-bg-secondary: #F9FAFB;
7
7
  --hb-text: #1F2937;
@@ -63,7 +63,9 @@
63
63
  bottom: 96px;
64
64
  right: 24px;
65
65
  width: 380px;
66
- height: 520px;
66
+ height: auto;
67
+ min-height: 240px;
68
+ max-height: calc(100vh - 120px);
67
69
  background: var(--hb-bg);
68
70
  border-radius: var(--hb-radius-dialog);
69
71
  box-shadow: var(--hb-shadow);
@@ -85,11 +87,11 @@
85
87
  display: flex;
86
88
  align-items: center;
87
89
  justify-content: space-between;
88
- padding: 16px;
90
+ padding: 12px 16px;
89
91
  border-bottom: 1px solid var(--hb-border);
90
92
  }
91
93
 
92
- .hb-header h2 { font-size: 16px; font-weight: 600; }
94
+ .hb-header h2 { font-size: 14px; font-weight: 500; }
93
95
 
94
96
  .hb-close {
95
97
  background: none;
@@ -98,8 +100,8 @@
98
100
  color: var(--hb-text-secondary);
99
101
  padding: 4px;
100
102
  border-radius: 4px;
101
- min-width: 44px;
102
- min-height: 44px;
103
+ min-width: 36px;
104
+ min-height: 36px;
103
105
  display: flex;
104
106
  align-items: center;
105
107
  justify-content: center;
@@ -288,18 +290,21 @@
288
290
  to { transform: translateY(0); }
289
291
  }
290
292
  }
291
- `;var u=[],b=[],E=!1,S=null,T=null;function L(){if(E)return;E=!0,S=console.error,console.error=(...e)=>{u.push({message:e.map(t=>String(t)).join(" "),timestamp:new Date().toISOString()}),u.length>20&&u.shift(),S.apply(console,e)},window.addEventListener("error",e=>{u.push({message:e.message,stack:e.error?.stack,timestamp:new Date().toISOString()}),u.length>20&&u.shift()}),window.addEventListener("unhandledrejection",e=>{u.push({message:`Unhandled rejection: ${String(e.reason)}`,timestamp:new Date().toISOString()}),u.length>20&&u.shift()}),T=window.fetch,window.fetch=async(...e)=>{try{let t=await T(...e);if(!t.ok){let r=typeof e[0]=="string"?e[0]:e[0]instanceof Request?e[0].url:String(e[0]),i=e[1];b.push({method:i?.method??"GET",url:r,status:t.status,statusText:t.statusText}),b.length>20&&b.shift()}return t}catch(t){let r=typeof e[0]=="string"?e[0]:e[0]instanceof Request?e[0].url:String(e[0]);throw b.push({method:e[1]?.method??"GET",url:r,statusText:String(t)}),b.length>20&&b.shift(),t}};let n=XMLHttpRequest.prototype.open,s=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(e,t,...r){return this._hbMethod=e,this._hbUrl=String(t),n.apply(this,[e,t,...r])},XMLHttpRequest.prototype.send=function(...e){return this.addEventListener("loadend",()=>{if(this.status>=400){let t=this;b.push({method:t._hbMethod??"GET",url:t._hbUrl??"",status:this.status,statusText:this.statusText}),b.length>20&&b.shift()}}),s.apply(this,e)}}function $(n){if(n.includes("Chrome")&&!n.includes("Edg")){let s=n.match(/Chrome\/([\d.]+)/);return s?`Chrome ${s[1]}`:"Chrome"}if(n.includes("Firefox")){let s=n.match(/Firefox\/([\d.]+)/);return s?`Firefox ${s[1]}`:"Firefox"}if(n.includes("Safari")&&!n.includes("Chrome")){let s=n.match(/Version\/([\d.]+)/);return s?`Safari ${s[1]}`:"Safari"}if(n.includes("Edg")){let s=n.match(/Edg\/([\d.]+)/);return s?`Edge ${s[1]}`:"Edge"}return"Unknown"}function W(n){if(n.includes("Mac OS X")){let s=n.match(/Mac OS X ([\d_.]+)/);return s?`macOS ${s[1].replace(/_/g,".")}`:"macOS"}if(n.includes("Windows NT")){let s=n.match(/Windows NT ([\d.]+)/);return s?`Windows ${s[1]}`:"Windows"}return n.includes("Linux")?"Linux":n.includes("Android")?"Android":n.includes("iPhone")||n.includes("iPad")?"iOS":"Unknown"}function M(){let n=navigator.userAgent;return{url:window.location.href,userAgent:n,browser:$(n),os:W(n),viewport:`${window.innerWidth}x${window.innerHeight}`,consoleErrors:[...u],failedRequests:[...b]}}async function F(){try{let n=await _();if(!n)return null;let s=await n(document.body,{logging:!1,useCORS:!0,allowTaint:!0,scale:1});return new Promise(e=>{s.toBlob(t=>e(t),"image/jpeg",.7)})}catch{return null}}async function _(){if(window.html2canvas)return window.html2canvas;try{return(await import("https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.esm.js")).default}catch{return new Promise(n=>{let s=document.createElement("script");s.src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js",s.onload=()=>{n(window.html2canvas)},s.onerror=()=>n(null),document.head.appendChild(s)})}}var k=class extends HTMLElement{constructor(){super();p(this,"shadow");p(this,"config");p(this,"currentScreen","trigger");p(this,"feedbackType","bug");p(this,"chatMessages",[]);p(this,"screenshotBlob",null);p(this,"duplicates",[]);p(this,"pendingReport",null);p(this,"isLoading",!1);this.shadow=this.attachShadow({mode:"open"}),this.config={endpoint:this.getAttribute("data-endpoint")??"/feedback",branding:this.getAttribute("data-branding")!=="false"}}connectedCallback(){L(),this.render()}render(){this.shadow.innerHTML="";let e=document.createElement("style");switch(e.textContent=C,this.shadow.appendChild(e),this.currentScreen){case"trigger":this.renderTrigger();break;case"mode-select":this.renderModeSelect();break;case"chat":this.renderChat();break;case"form":this.renderForm();break;case"duplicates":this.renderDuplicates();break;case"success":this.renderSuccess();break}}renderTrigger(){let e=document.createElement("button");e.className="hb-trigger",e.setAttribute("aria-label","Report feedback"),e.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>',e.addEventListener("click",()=>this.navigate("mode-select")),this.shadow.appendChild(e)}renderDialog(e,t,r){this.renderTrigger();let i=document.createElement("div");i.className="hb-dialog",i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true"),i.setAttribute("aria-label",e);let o=document.createElement("div");o.className="hb-header";let d=document.createElement("h2");d.textContent=e;let a=document.createElement("button");a.className="hb-close",a.setAttribute("aria-label","Close"),a.innerHTML="&times;",a.addEventListener("click",()=>this.navigate("trigger")),o.appendChild(d),o.appendChild(a),i.appendChild(o);let c=document.createElement("div");if(c.className="hb-body",c.appendChild(t),i.appendChild(c),r&&i.appendChild(r),this.config.branding){let l=document.createElement("div");l.className="hb-footer",l.innerHTML='Powered by <a href="https://github.com/hearback/hearback" target="_blank" rel="noopener">hearback</a>',i.appendChild(l)}this.shadow.appendChild(i),a.focus(),i.addEventListener("keydown",l=>{l.key==="Escape"&&this.navigate("trigger")})}renderModeSelect(){let e=document.createElement("div");e.className="hb-mode-select";let t=this.createModeButton("\u{1F41B}","Bug Report","Something is broken or not working right",()=>{this.feedbackType="bug",this.startFeedback()}),r=this.createModeButton("\u{1F4A1}","Feature Request","Suggest an improvement or new feature",()=>{this.feedbackType="feature",this.startFeedback()});e.appendChild(t),e.appendChild(r),this.renderDialog("Send Feedback",e)}createModeButton(e,t,r,i){let o=document.createElement("button");return o.className="hb-mode-btn",o.innerHTML=`
293
+ `;var u=[],b=[],E=!1,S=null,T=null;function L(){if(E)return;E=!0,S=console.error,console.error=(...e)=>{u.push({message:e.map(t=>String(t)).join(" "),timestamp:new Date().toISOString()}),u.length>20&&u.shift(),S.apply(console,e)},window.addEventListener("error",e=>{u.push({message:e.message,stack:e.error?.stack,timestamp:new Date().toISOString()}),u.length>20&&u.shift()}),window.addEventListener("unhandledrejection",e=>{u.push({message:`Unhandled rejection: ${String(e.reason)}`,timestamp:new Date().toISOString()}),u.length>20&&u.shift()}),T=window.fetch,window.fetch=async(...e)=>{try{let t=await T(...e);if(!t.ok){let s=typeof e[0]=="string"?e[0]:e[0]instanceof Request?e[0].url:String(e[0]),i=e[1];b.push({method:i?.method??"GET",url:s,status:t.status,statusText:t.statusText}),b.length>20&&b.shift()}return t}catch(t){let s=typeof e[0]=="string"?e[0]:e[0]instanceof Request?e[0].url:String(e[0]);throw b.push({method:e[1]?.method??"GET",url:s,statusText:String(t)}),b.length>20&&b.shift(),t}};let n=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(e,t,...s){return this._hbMethod=e,this._hbUrl=String(t),n.apply(this,[e,t,...s])},XMLHttpRequest.prototype.send=function(...e){return this.addEventListener("loadend",()=>{if(this.status>=400){let t=this;b.push({method:t._hbMethod??"GET",url:t._hbUrl??"",status:this.status,statusText:this.statusText}),b.length>20&&b.shift()}}),r.apply(this,e)}}function q(n){if(n.includes("Chrome")&&!n.includes("Edg")){let r=n.match(/Chrome\/([\d.]+)/);return r?`Chrome ${r[1]}`:"Chrome"}if(n.includes("Firefox")){let r=n.match(/Firefox\/([\d.]+)/);return r?`Firefox ${r[1]}`:"Firefox"}if(n.includes("Safari")&&!n.includes("Chrome")){let r=n.match(/Version\/([\d.]+)/);return r?`Safari ${r[1]}`:"Safari"}if(n.includes("Edg")){let r=n.match(/Edg\/([\d.]+)/);return r?`Edge ${r[1]}`:"Edge"}return"Unknown"}function W(n){if(n.includes("Mac OS X")){let r=n.match(/Mac OS X ([\d_.]+)/);return r?`macOS ${r[1].replace(/_/g,".")}`:"macOS"}if(n.includes("Windows NT")){let r=n.match(/Windows NT ([\d.]+)/);return r?`Windows ${r[1]}`:"Windows"}return n.includes("Linux")?"Linux":n.includes("Android")?"Android":n.includes("iPhone")||n.includes("iPad")?"iOS":"Unknown"}function M(){let n=navigator.userAgent;return{url:window.location.href,userAgent:n,browser:q(n),os:W(n),viewport:`${window.innerWidth}x${window.innerHeight}`,consoleErrors:[...u],failedRequests:[...b]}}async function F(){try{let n=await _();if(!n)return null;let r=await n(document.body,{logging:!1,useCORS:!0,allowTaint:!0,scale:1});return new Promise(e=>{r.toBlob(t=>e(t),"image/jpeg",.7)})}catch{return null}}async function _(){if(window.html2canvas)return window.html2canvas;try{return(await import("https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.esm.js")).default}catch{return new Promise(n=>{let r=document.createElement("script");r.src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js",r.onload=()=>{n(window.html2canvas)},r.onerror=()=>n(null),document.head.appendChild(r)})}}function z(n){if(!n)return!1;let r=n.trim();return!r||r.length>64||/[;{}<>]/.test(r)?!1:!!(/^#[0-9A-Fa-f]{3,8}$/.test(r)||/^(?:rgb|rgba|hsl|hsla|oklch|oklab|color-mix)\([^)]+\)$/i.test(r)||/^[a-z]+$/i.test(r))}var k=class extends HTMLElement{constructor(){super();p(this,"shadow");p(this,"config");p(this,"currentScreen","trigger");p(this,"feedbackType","bug");p(this,"chatMessages",[]);p(this,"screenshotBlob",null);p(this,"duplicates",[]);p(this,"pendingReport",null);p(this,"isLoading",!1);this.shadow=this.attachShadow({mode:"open"}),this.config={endpoint:"/feedback",branding:!0}}connectedCallback(){let e=this.getAttribute("data-accent");this.config={endpoint:this.getAttribute("data-endpoint")??"/feedback",branding:this.getAttribute("data-branding")!=="false",accent:z(e)?e:void 0},L(),this.render()}render(){this.shadow.innerHTML="";let e=document.createElement("style");if(e.textContent=C,this.shadow.appendChild(e),this.config.accent){let t=document.createElement("style");t.textContent=`:host {
294
+ --hb-accent: ${this.config.accent};
295
+ --hb-accent-hover: color-mix(in srgb, ${this.config.accent} 85%, black);
296
+ }`,this.shadow.appendChild(t)}switch(this.currentScreen){case"trigger":this.renderTrigger();break;case"mode-select":this.renderModeSelect();break;case"chat":this.renderChat();break;case"form":this.renderForm();break;case"duplicates":this.renderDuplicates();break;case"success":this.renderSuccess();break}}renderTrigger(){let e=document.createElement("button");e.className="hb-trigger",e.setAttribute("aria-label","Report feedback"),e.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>',e.addEventListener("click",()=>this.navigate("mode-select")),this.shadow.appendChild(e)}renderDialog(e,t,s){this.renderTrigger();let i=document.createElement("div");i.className="hb-dialog",i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true"),i.setAttribute("aria-label",e);let o=document.createElement("div");o.className="hb-header";let c=document.createElement("h2");c.textContent=e;let a=document.createElement("button");a.className="hb-close",a.setAttribute("aria-label","Close"),a.innerHTML="&times;",a.addEventListener("click",()=>this.navigate("trigger")),o.appendChild(c),o.appendChild(a),i.appendChild(o);let d=document.createElement("div");if(d.className="hb-body",d.appendChild(t),i.appendChild(d),s&&i.appendChild(s),this.config.branding){let l=document.createElement("div");l.className="hb-footer",l.innerHTML='Powered by <a href="https://first-tree.ai/" target="_blank" rel="noopener">first-tree</a>',i.appendChild(l)}this.shadow.appendChild(i),a.focus(),i.addEventListener("keydown",l=>{l.key==="Escape"&&this.navigate("trigger")})}renderModeSelect(){let e=document.createElement("div");e.className="hb-mode-select";let t=this.createModeButton("\u{1F41B}","Bug Report","Something is broken or not working right",()=>{this.feedbackType="bug",this.startFeedback()}),s=this.createModeButton("\u{1F4A1}","Feature Request","Suggest an improvement or new feature",()=>{this.feedbackType="feature",this.startFeedback()});e.appendChild(t),e.appendChild(s),this.renderDialog("Send Feedback",e)}createModeButton(e,t,s,i){let o=document.createElement("button");return o.className="hb-mode-btn",o.innerHTML=`
292
297
  <span class="hb-mode-icon">${e}</span>
293
298
  <span>
294
299
  <span class="hb-mode-label">${t}</span><br/>
295
- <span class="hb-mode-desc">${r}</span>
300
+ <span class="hb-mode-desc">${s}</span>
296
301
  </span>
297
- `,o.addEventListener("click",i),o}async startFeedback(){try{if((await fetch(`${this.config.endpoint}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:[]})})).status===501){this.navigate("form");return}}catch{this.navigate("form");return}this.chatMessages=[],this.navigate("chat")}renderChat(){let e=document.createElement("div");e.className="hb-messages",e.setAttribute("role","log"),e.setAttribute("aria-live","polite"),this.chatMessages.length===0&&this.chatMessages.push({role:"assistant",content:this.feedbackType==="bug"?"Hi! What issue are you experiencing?":"Hi! What feature or improvement would you like to suggest?"});for(let a of this.chatMessages){let c=document.createElement("div");c.className=`hb-msg hb-msg-${a.role}`,c.textContent=a.content,e.appendChild(c)}if(this.isLoading){let a=document.createElement("div");a.className="hb-typing",a.innerHTML="<span></span><span></span><span></span>",e.appendChild(a)}let t=document.createElement("div");t.className="hb-input-bar";let r=document.createElement("button");r.className="hb-screenshot-btn",r.textContent="\u{1F4F7}",r.title="Take screenshot",r.addEventListener("click",()=>this.takeScreenshot());let i=document.createElement("input");i.type="text",i.placeholder="Type your message...",i.addEventListener("keydown",a=>{a.key==="Enter"&&i.value.trim()&&(this.sendChatMessage(i.value.trim()),i.value="")});let o=document.createElement("button");o.className="hb-send",o.textContent="Send",o.disabled=this.isLoading,o.addEventListener("click",()=>{i.value.trim()&&(this.sendChatMessage(i.value.trim()),i.value="")}),t.appendChild(r),t.appendChild(i),t.appendChild(o),this.renderDialog("Report Feedback",e,t);let d=this.shadow.querySelector(".hb-body");d&&(d.scrollTop=d.scrollHeight),i.focus()}async sendChatMessage(e){this.chatMessages.push({role:"user",content:e}),this.isLoading=!0,this.render();try{let t=await fetch(`${this.config.endpoint}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:this.chatMessages.map(d=>({role:d.role,content:d.content}))})});if(t.status===501){this.navigate("form");return}if(t.status===429){this.chatMessages.push({role:"assistant",content:"Too many messages. Please try again later."}),this.isLoading=!1,this.render();return}if(!t.ok){this.chatMessages.push({role:"assistant",content:"Let me switch to a form instead."}),this.isLoading=!1,this.navigate("form");return}let r=await this.readStream(t),i=r.match(/```json\s*({[\s\S]*?})\s*```/);if(i)try{let d=JSON.parse(i[1]);if(d.ready&&d.title&&d.description){this.feedbackType=d.type==="feature"?"feature":"bug",this.pendingReport={title:d.title,description:d.description},this.chatMessages.push({role:"assistant",content:"Got it! Submitting your feedback now..."}),this.isLoading=!1,this.render(),await this.submitReport();return}}catch{}let o=r.replace(/```json[\s\S]*?```/g,"").trim();this.chatMessages.push({role:"assistant",content:o||r})}catch{this.chatMessages.push({role:"assistant",content:"Something went wrong. Let me use a form instead."}),this.navigate("form");return}this.isLoading=!1,this.render()}async readStream(e){if(!(e.headers.get("content-type")??"").includes("text/event-stream"))return(await e.json()).choices?.[0]?.message?.content??"";let r=e.body?.getReader();if(!r)return"";let i=new TextDecoder,o="",d=0,a=15e3,c=new Promise((l,h)=>{d=window.setTimeout(()=>h(new Error("timeout")),a)});try{await Promise.race([(async()=>{for(;;){let{done:l,value:h}=await r.read();if(l)break;let f=i.decode(h,{stream:!0});for(let x of f.split(`
298
- `))if(x.startsWith("data: ")){let g=x.slice(6);if(g==="[DONE]")break;try{let m=JSON.parse(g).choices?.[0]?.delta?.content;m&&(o+=m)}catch{}}}})(),c])}catch{}finally{clearTimeout(d),r.releaseLock()}return o||"I had trouble processing that. Could you try describing the issue in a different way?"}renderForm(){let e=document.createElement("div");e.className="hb-form";let t=document.createElement("div"),r=document.createElement("label");r.textContent="Type";let i=document.createElement("select");i.innerHTML=`
302
+ `,o.addEventListener("click",i),o}async startFeedback(){try{if((await fetch(`${this.config.endpoint}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:[]})})).status===501){this.navigate("form");return}}catch{this.navigate("form");return}this.chatMessages=[],this.navigate("chat")}renderChat(){let e=document.createElement("div");e.className="hb-messages",e.setAttribute("role","log"),e.setAttribute("aria-live","polite"),this.chatMessages.length===0&&this.chatMessages.push({role:"assistant",content:this.feedbackType==="bug"?"Hi! What issue are you experiencing?":"Hi! What feature or improvement would you like to suggest?"});for(let a of this.chatMessages){let d=document.createElement("div");d.className=`hb-msg hb-msg-${a.role}`,d.textContent=a.content,e.appendChild(d)}if(this.isLoading){let a=document.createElement("div");a.className="hb-typing",a.innerHTML="<span></span><span></span><span></span>",e.appendChild(a)}let t=document.createElement("div");t.className="hb-input-bar";let s=document.createElement("button");s.className="hb-screenshot-btn",s.textContent="\u{1F4F7}",s.title="Take screenshot",s.addEventListener("click",()=>this.takeScreenshot());let i=document.createElement("input");i.type="text",i.placeholder="Type your message...",i.addEventListener("keydown",a=>{a.key==="Enter"&&i.value.trim()&&(this.sendChatMessage(i.value.trim()),i.value="")});let o=document.createElement("button");o.className="hb-send",o.textContent="Send",o.disabled=this.isLoading,o.addEventListener("click",()=>{i.value.trim()&&(this.sendChatMessage(i.value.trim()),i.value="")}),t.appendChild(s),t.appendChild(i),t.appendChild(o),this.renderDialog("Report Feedback",e,t);let c=this.shadow.querySelector(".hb-body");c&&(c.scrollTop=c.scrollHeight),i.focus()}async sendChatMessage(e){this.chatMessages.push({role:"user",content:e}),this.isLoading=!0,this.render();try{let t=await fetch(`${this.config.endpoint}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:this.chatMessages.map(c=>({role:c.role,content:c.content}))})});if(t.status===501){this.navigate("form");return}if(t.status===429){this.chatMessages.push({role:"assistant",content:"Too many messages. Please try again later."}),this.isLoading=!1,this.render();return}if(!t.ok){this.chatMessages.push({role:"assistant",content:"Let me switch to a form instead."}),this.isLoading=!1,this.navigate("form");return}let s=await this.readStream(t),i=s.match(/```json\s*({[\s\S]*?})\s*```/);if(i)try{let c=JSON.parse(i[1]);if(c.ready&&c.title&&c.description){this.feedbackType=c.type==="feature"?"feature":"bug",this.pendingReport={title:c.title,description:c.description},this.chatMessages.push({role:"assistant",content:"Got it! Submitting your feedback now..."}),this.isLoading=!1,this.render(),await this.submitReport();return}}catch{}let o=s.replace(/```json[\s\S]*?```/g,"").trim();this.chatMessages.push({role:"assistant",content:o||s})}catch{this.chatMessages.push({role:"assistant",content:"Something went wrong. Let me use a form instead."}),this.navigate("form");return}this.isLoading=!1,this.render()}async readStream(e){if(!(e.headers.get("content-type")??"").includes("text/event-stream"))return(await e.json()).choices?.[0]?.message?.content??"";let s=e.body?.getReader();if(!s)return"";let i=new TextDecoder,o="",c=0,a=15e3,d=new Promise((l,h)=>{c=window.setTimeout(()=>h(new Error("timeout")),a)});try{await Promise.race([(async()=>{for(;;){let{done:l,value:h}=await s.read();if(l)break;let f=i.decode(h,{stream:!0});for(let x of f.split(`
303
+ `))if(x.startsWith("data: ")){let g=x.slice(6);if(g==="[DONE]")break;try{let m=JSON.parse(g).choices?.[0]?.delta?.content;m&&(o+=m)}catch{}}}})(),d])}catch{}finally{clearTimeout(c),s.releaseLock()}return o||"I had trouble processing that. Could you try describing the issue in a different way?"}renderForm(){let e=document.createElement("div");e.className="hb-form";let t=document.createElement("div"),s=document.createElement("label");s.textContent="Type";let i=document.createElement("select");i.innerHTML=`
299
304
  <option value="bug" ${this.feedbackType==="bug"?"selected":""}>Bug Report</option>
300
305
  <option value="feature" ${this.feedbackType==="feature"?"selected":""}>Feature Request</option>
301
- `,i.addEventListener("change",()=>{this.feedbackType=i.value}),t.appendChild(r),t.appendChild(i);let o=document.createElement("div"),d=document.createElement("label");d.textContent="Title *";let a=document.createElement("input");a.type="text",a.placeholder=this.feedbackType==="bug"?"What went wrong?":"What would you like?",a.required=!0,a.minLength=5,o.appendChild(d),o.appendChild(a);let c=document.createElement("div"),l=document.createElement("label");l.textContent="Description *";let h=document.createElement("textarea");h.placeholder=this.feedbackType==="bug"?"Steps to reproduce, expected vs actual behavior...":"Describe the feature and your use case...",h.required=!0,c.appendChild(l),c.appendChild(h);let f=document.createElement("div"),x=document.createElement("label");x.textContent="Email (optional \u2014 for fix notifications)";let g=document.createElement("input");g.type="email",g.placeholder="you@example.com",f.appendChild(x),f.appendChild(g);let v=document.createElement("button");v.className="hb-screenshot-btn",v.textContent=this.screenshotBlob?"\u2705 Screenshot attached (retake?)":"\u{1F4F7} Attach screenshot",v.addEventListener("click",()=>this.takeScreenshot());let m=document.createElement("button");m.className="hb-submit",m.textContent="Submit",m.addEventListener("click",async()=>{if(a.value.trim().length<5){a.setCustomValidity("Title must be at least 5 characters"),a.reportValidity();return}if(!h.value.trim()){h.setCustomValidity("Description is required"),h.reportValidity();return}this.pendingReport={title:a.value.trim(),description:h.value.trim()},m.disabled=!0,m.textContent="Submitting...",await this.submitReport(g.value.trim()||void 0)}),e.appendChild(t),e.appendChild(o),e.appendChild(c),e.appendChild(f),e.appendChild(v),e.appendChild(m),this.renderDialog("Submit Feedback",e)}renderDuplicates(){let e=document.createElement("div"),t=document.createElement("p");t.textContent="We found similar existing issues:",t.style.marginBottom="12px",e.appendChild(t);let r=document.createElement("div");r.className="hb-duplicates";for(let o of this.duplicates){let d=document.createElement("div");d.className="hb-dup-item";let a=document.createElement("span");a.className="hb-dup-title",a.textContent=`#${o.issueNumber}: ${o.title}`;let c=document.createElement("button");c.className="hb-dup-sub",c.textContent="Me too",c.addEventListener("click",()=>this.subscribeToDuplicate(o.issueNumber,c)),d.appendChild(a),d.appendChild(c),r.appendChild(d)}e.appendChild(r);let i=document.createElement("button");i.className="hb-submit",i.textContent="Not a duplicate \u2014 submit anyway",i.style.marginTop="16px",i.addEventListener("click",()=>this.submitReport(void 0,!0)),e.appendChild(i),this.renderDialog("Similar Issues Found",e)}renderSuccess(){let e=document.createElement("div");e.className="hb-success",e.innerHTML=`
306
+ `,i.addEventListener("change",()=>{this.feedbackType=i.value}),t.appendChild(s),t.appendChild(i);let o=document.createElement("div"),c=document.createElement("label");c.textContent="Title *";let a=document.createElement("input");a.type="text",a.placeholder=this.feedbackType==="bug"?"What went wrong?":"What would you like?",a.required=!0,a.minLength=5,o.appendChild(c),o.appendChild(a);let d=document.createElement("div"),l=document.createElement("label");l.textContent="Description *";let h=document.createElement("textarea");h.placeholder=this.feedbackType==="bug"?"Steps to reproduce, expected vs actual behavior...":"Describe the feature and your use case...",h.required=!0,d.appendChild(l),d.appendChild(h);let f=document.createElement("div"),x=document.createElement("label");x.textContent="Email (optional \u2014 for fix notifications)";let g=document.createElement("input");g.type="email",g.placeholder="you@example.com",f.appendChild(x),f.appendChild(g);let v=document.createElement("button");v.className="hb-screenshot-btn",v.textContent=this.screenshotBlob?"\u2705 Screenshot attached (retake?)":"\u{1F4F7} Attach screenshot",v.addEventListener("click",()=>this.takeScreenshot());let m=document.createElement("button");m.className="hb-submit",m.textContent="Submit",m.addEventListener("click",async()=>{if(a.value.trim().length<5){a.setCustomValidity("Title must be at least 5 characters"),a.reportValidity();return}if(!h.value.trim()){h.setCustomValidity("Description is required"),h.reportValidity();return}this.pendingReport={title:a.value.trim(),description:h.value.trim()},m.disabled=!0,m.textContent="Submitting...",await this.submitReport(g.value.trim()||void 0)}),e.appendChild(t),e.appendChild(o),e.appendChild(d),e.appendChild(f),e.appendChild(v),e.appendChild(m),this.renderDialog("Submit Feedback",e)}renderDuplicates(){let e=document.createElement("div"),t=document.createElement("p");t.textContent="We found similar existing issues:",t.style.marginBottom="12px",e.appendChild(t);let s=document.createElement("div");s.className="hb-duplicates";for(let o of this.duplicates){let c=document.createElement("div");c.className="hb-dup-item";let a=document.createElement("span");a.className="hb-dup-title",a.textContent=`#${o.issueNumber}: ${o.title}`;let d=document.createElement("button");d.className="hb-dup-sub",d.textContent="Me too",d.addEventListener("click",()=>this.subscribeToDuplicate(o.issueNumber,d)),c.appendChild(a),c.appendChild(d),s.appendChild(c)}e.appendChild(s);let i=document.createElement("button");i.className="hb-submit",i.textContent="Not a duplicate \u2014 submit anyway",i.style.marginTop="16px",i.addEventListener("click",()=>this.submitReport(void 0,!0)),e.appendChild(i),this.renderDialog("Similar Issues Found",e)}renderSuccess(){let e=document.createElement("div");e.className="hb-success",e.innerHTML=`
302
307
  <div class="hb-check">\u2705</div>
303
308
  <h3>Feedback submitted!</h3>
304
309
  <p>We'll notify you when it's addressed.</p>
305
- `;let t=document.createElement("button");t.className="hb-submit",t.textContent="Close",t.style.marginTop="24px",t.addEventListener("click",()=>this.navigate("trigger")),e.appendChild(t),this.renderDialog("Thank You",e)}navigate(e){this.currentScreen=e,this.render()}async takeScreenshot(){let e=this.shadow.querySelector(".hb-dialog");e&&(e.style.display="none");let t=this.shadow.querySelector(".hb-trigger");t&&(t.style.display="none"),await new Promise(r=>setTimeout(r,100)),this.screenshotBlob=await F(),e&&(e.style.display=""),t&&(t.style.display=""),this.render()}async uploadScreenshot(){if(this.screenshotBlob)try{let e=new FormData;e.append("screenshot",this.screenshotBlob,"screenshot.jpg");let t=await fetch(`${this.config.endpoint}/upload`,{method:"POST",body:e});return t.ok?(await t.json()).url:void 0}catch{return}}async submitReport(e,t=!1){if(!this.pendingReport)return;let r=M(),i=await this.uploadScreenshot();try{let o=await fetch(`${this.config.endpoint}/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:this.feedbackType,title:this.pendingReport.title,description:this.pendingReport.description,context:{source:"web-plugin",browser:{url:r.url,userAgent:r.userAgent,browser:r.browser,os:r.os,viewport:r.viewport},consoleErrors:r.consoleErrors,failedRequests:r.failedRequests,screenshotUrl:i,timestamp:new Date().toISOString()},reporterEmail:e,skipDuplicateCheck:t})});if(!o.ok){let a=await o.json();alert(a.error??"Submission failed. Please try again.");return}let d=await o.json();if(d.isDuplicate&&d.candidates){this.duplicates=d.candidates,this.navigate("duplicates");return}this.navigate("success")}catch{alert("Network error. Please try again.")}}async subscribeToDuplicate(e,t){let r=prompt("Enter your email to get notified:");if(r){t.disabled=!0,t.textContent="Subscribing...";try{(await fetch(`${this.config.endpoint}/subscribe`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({issueNumber:e,email:r})})).ok?t.textContent=`Subscribed to #${e}`:(t.textContent="Failed",t.disabled=!1)}catch{t.textContent="Failed",t.disabled=!1}}}};customElements.get("hearback-widget")||customElements.define("hearback-widget",k);var w=document.currentScript;function R(){if(!w)return;let n=w.getAttribute("data-endpoint");if(!n)return;let s=document.createElement("hearback-widget");s.setAttribute("data-endpoint",n);let e=w.getAttribute("data-branding");e&&s.setAttribute("data-branding",e),document.body.appendChild(s)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",R):R();})();
310
+ `;let t=document.createElement("button");t.className="hb-submit",t.textContent="Close",t.style.marginTop="24px",t.addEventListener("click",()=>this.navigate("trigger")),e.appendChild(t),this.renderDialog("Thank You",e)}navigate(e){this.currentScreen=e,this.render()}async takeScreenshot(){let e=this.shadow.querySelector(".hb-dialog");e&&(e.style.display="none");let t=this.shadow.querySelector(".hb-trigger");t&&(t.style.display="none"),await new Promise(i=>setTimeout(i,100)),this.screenshotBlob=await F(),e&&(e.style.display=""),t&&(t.style.display="");let s=this.shadow.querySelector(".hb-screenshot-btn");s&&(this.currentScreen==="form"?s.textContent=this.screenshotBlob?"\u2705 Screenshot attached (retake?)":"\u{1F4F7} Attach screenshot":s.title=this.screenshotBlob?"Screenshot attached (click to retake)":"Take screenshot")}async uploadScreenshot(){if(this.screenshotBlob)try{let e=new FormData;e.append("screenshot",this.screenshotBlob,"screenshot.jpg");let t=await fetch(`${this.config.endpoint}/upload`,{method:"POST",body:e});return t.ok?(await t.json()).url:void 0}catch{return}}async submitReport(e,t=!1){if(!this.pendingReport)return;let s=M(),i=await this.uploadScreenshot();try{let o=await fetch(`${this.config.endpoint}/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:this.feedbackType,title:this.pendingReport.title,description:this.pendingReport.description,context:{source:"web-plugin",browser:{url:s.url,userAgent:s.userAgent,browser:s.browser,os:s.os,viewport:s.viewport},consoleErrors:s.consoleErrors,failedRequests:s.failedRequests,screenshotUrl:i,timestamp:new Date().toISOString()},reporterEmail:e,skipDuplicateCheck:t})});if(!o.ok){let a=await o.json();alert(a.error??"Submission failed. Please try again.");return}let c=await o.json();if(c.isDuplicate&&c.candidates){this.duplicates=c.candidates,this.navigate("duplicates");return}this.navigate("success")}catch{alert("Network error. Please try again.")}}async subscribeToDuplicate(e,t){let s=prompt("Enter your email to get notified:");if(s){t.disabled=!0,t.textContent="Subscribing...";try{(await fetch(`${this.config.endpoint}/subscribe`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({issueNumber:e,email:s})})).ok?t.textContent=`Subscribed to #${e}`:(t.textContent="Failed",t.disabled=!1)}catch{t.textContent="Failed",t.disabled=!1}}}};customElements.get("hearback-widget")||customElements.define("hearback-widget",k);var y=document.currentScript;function B(){if(!y)return;let n=y.getAttribute("data-endpoint");if(!n)return;let r=document.createElement("hearback-widget");r.setAttribute("data-endpoint",n);let e=y.getAttribute("data-branding");e&&r.setAttribute("data-branding",e);let t=y.getAttribute("data-accent");t&&r.setAttribute("data-accent",t),document.body.appendChild(r)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",B):B();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hearback-widget",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Embeddable feedback widget for any website — Shadow DOM, <25KB, dual mode (AI chat + form), screenshot, auto-captures console/network context",
5
5
  "type": "module",
6
6
  "main": "./dist/widget.js",