jat-feedback 3.0.0 → 3.0.1

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.
@@ -29,7 +29,7 @@ var hr=Object.defineProperty;var ir=Ce=>{throw TypeError(Ce)};var gr=(Ce,Re,De)=
29
29
  z-index: 2147483646;
30
30
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
31
31
  pointer-events: none;
32
- `,document.body.appendChild(e),e}function handleHover(e){if(!isActive||!overlay)return;const t=e.target;if(t===overlay||t.id==="jat-feedback-picker-tooltip")return;const n=t.getBoundingClientRect();overlay.style.top=`${n.top}px`,overlay.style.left=`${n.left}px`,overlay.style.width=`${n.width}px`,overlay.style.height=`${n.height}px`}function handleClick(e){var s;if(!isActive)return;e.preventDefault(),e.stopPropagation();const t=e.target,n=t.getBoundingClientRect(),r=onSelect;stopElementPicker();const o={tagName:t.tagName,className:typeof t.className=="string"?t.className:"",id:t.id,textContent:((s=t.textContent)==null?void 0:s.substring(0,100))||"",attributes:Array.from(t.attributes).reduce((i,a)=>(i[a.name]=a.value,i),{}),xpath:getXPath(t),selector:generateSelector(t),boundingRect:{x:n.x,y:n.y,width:n.width,height:n.height,top:n.top,left:n.left,bottom:n.bottom,right:n.right},screenshot:null,timestamp:new Date().toISOString(),url:window.location.href};r==null||r(o)}function handleEscape(e){e.key==="Escape"&&stopElementPicker()}function startElementPicker(e){isActive||(isActive=!0,onSelect=e,originalCursor=document.body.style.cursor,document.body.style.cursor="crosshair",overlay=createOverlay(),tooltip=showTooltip(),document.addEventListener("mousemove",handleHover,!0),document.addEventListener("click",handleClick,!0),document.addEventListener("keydown",handleEscape,!0))}function stopElementPicker(){isActive&&(isActive=!1,onSelect=null,document.body.style.cursor=originalCursor,overlay&&(overlay.remove(),overlay=null),tooltip&&(tooltip.remove(),tooltip=null),document.removeEventListener("mousemove",handleHover,!0),document.removeEventListener("click",handleClick,!0),document.removeEventListener("keydown",handleEscape,!0))}function isElementPickerActive(){return isActive}const ANNOTATION_COLORS=["#ef4444","#eab308","#22c55e","#3b82f6","#ffffff","#111827"],DEFAULT_STROKE_WIDTH=3;let _editorOpen=!1;function setAnnotationEditorOpen(e){_editorOpen=e}function isAnnotationEditorOpen(){return _editorOpen}let _nextId=1;function nextShapeId(){return _nextId++}function drawArrow(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(n.x,n.y),e.lineTo(r.x,r.y),e.stroke();const i=Math.atan2(r.y-n.y,r.x-n.x),a=14,l=Math.PI/7;e.beginPath(),e.moveTo(r.x,r.y),e.lineTo(r.x-a*Math.cos(i-l),r.y-a*Math.sin(i-l)),e.moveTo(r.x,r.y),e.lineTo(r.x-a*Math.cos(i+l),r.y-a*Math.sin(i+l)),e.stroke()}function drawRectangle(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s,e.lineJoin="round";const i=Math.min(n.x,r.x),a=Math.min(n.y,r.y),l=Math.abs(r.x-n.x),c=Math.abs(r.y-n.y);e.strokeRect(i,a,l,c)}function drawEllipse(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s;const i=(n.x+r.x)/2,a=(n.y+r.y)/2,l=Math.abs(r.x-n.x)/2,c=Math.abs(r.y-n.y)/2;l<1||c<1||(e.beginPath(),e.ellipse(i,a,l,c,0,0,Math.PI*2),e.stroke())}function drawFreehand(e,t){const{points:n,color:r,strokeWidth:o}=t;if(!(n.length<2)){e.strokeStyle=r,e.lineWidth=o,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(n[0].x,n[0].y);for(let s=1;s<n.length;s++)e.lineTo(n[s].x,n[s].y);e.stroke()}}function drawText(e,t){const{position:n,content:r,color:o,fontSize:s}=t;r&&(e.font=`bold ${s}px sans-serif`,e.textBaseline="top",e.strokeStyle="#000000",e.lineWidth=2,e.lineJoin="round",e.strokeText(r,n.x,n.y),e.fillStyle=o,e.fillText(r,n.x,n.y))}function renderShape(e,t){switch(e.save(),t.type){case"arrow":drawArrow(e,t);break;case"rectangle":drawRectangle(e,t);break;case"ellipse":drawEllipse(e,t);break;case"freehand":drawFreehand(e,t);break;case"text":drawText(e,t);break}e.restore()}function renderAllShapes(e,t){for(const n of t)renderShape(e,n)}function mergeAnnotation(e,t,n,r){return new Promise((o,s)=>{const i=new Image;i.onload=()=>{const a=document.createElement("canvas");a.width=n,a.height=r;const l=a.getContext("2d");if(!l){s(new Error("Canvas context unavailable"));return}l.drawImage(i,0,0,n,r),renderAllShapes(l,t),o(a.toDataURL("image/jpeg",.85))},i.onerror=()=>s(new Error("Failed to load image")),i.src=e})}async function submitReport(e,t){const n=`${e.replace(/\/$/,"")}/api/feedback/report`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),o=await r.json();return r.ok?{ok:!0,id:o.id}:{ok:!1,error:o.error||`HTTP ${r.status}`}}async function fetchReports(e){try{const t=`${e.replace(/\/$/,"")}/api/feedback/reports`,n=await fetch(t,{method:"GET",credentials:"same-origin"});if(!n.ok){const o=await n.json().catch(()=>({error:`HTTP ${n.status}`}));return{reports:[],error:o.error||`HTTP ${n.status}`}}return{reports:(await n.json()).reports||[]}}catch(t){return{reports:[],error:t instanceof Error?t.message:"Failed to fetch"}}}async function respondToReport(e,t,n,r,o){try{const s=`${e.replace(/\/$/,"")}/api/feedback/reports/${t}/respond`,i={response:n};r&&(i.reason=r),o!=null&&o.screenshots&&o.screenshots.length>0&&(i.screenshots=o.screenshots),o!=null&&o.elements&&o.elements.length>0&&(i.elements=o.elements);const a=await fetch(s,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}),l=await a.json();return a.ok?{ok:!0}:{ok:!1,error:l.error||`HTTP ${a.status}`}}catch(s){return{ok:!1,error:s instanceof Error?s.message:"Failed to respond"}}}const notesUrl=e=>`${e.replace(/\/$/,"")}/api/feedback/notes`;async function fetchNotes(e,t,n){try{let r=`${notesUrl(e)}?project=${encodeURIComponent(t)}`;const o=await fetch(r,{credentials:"same-origin"});if(!o.ok){const i=await o.json().catch(()=>({error:`HTTP ${o.status}`}));return{notes:[],error:i.error||`HTTP ${o.status}`}}return{notes:(await o.json()).notes||[]}}catch(r){return{notes:[],error:r instanceof Error?r.message:"Failed to fetch notes"}}}async function createNote(e,t){try{const n=await fetch(notesUrl(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json();return n.ok?{ok:!0,note:r.note}:{ok:!1,error:r.error||`HTTP ${n.status}`}}catch(n){return{ok:!1,error:n instanceof Error?n.message:"Failed to create note"}}}async function updateNote(e,t,n){try{const r=await fetch(`${notesUrl(e)}/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}),o=await r.json();return r.ok?{ok:!0}:{ok:!1,error:o.error||`HTTP ${r.status}`}}catch(r){return{ok:!1,error:r instanceof Error?r.message:"Failed to update note"}}}async function deleteNote(e,t){try{const n=await fetch(`${notesUrl(e)}/${t}`,{method:"DELETE"}),r=await n.json();return n.ok?{ok:!0}:{ok:!1,error:r.error||`HTTP ${n.status}`}}catch(n){return{ok:!1,error:n instanceof Error?n.message:"Failed to delete note"}}}const STORAGE_KEY="jat-feedback-queue",MAX_ENTRIES=50,RETRY_INTERVAL_MS=3e4;let retryTimer=null;function getQueue(){try{const e=localStorage.getItem(STORAGE_KEY);return e?JSON.parse(e):[]}catch{return[]}}function saveQueue(e){try{localStorage.setItem(STORAGE_KEY,JSON.stringify(e))}catch{}}function enqueue(e,t){const n=getQueue();for(n.push({report:t,endpoint:e,queuedAt:new Date().toISOString(),attempts:0});n.length>MAX_ENTRIES;)n.shift();saveQueue(n)}async function processQueue(){const e=getQueue();if(e.length===0)return;const t=[];for(const n of e)try{(await submitReport(n.endpoint,n.report)).ok||(n.attempts++,t.push(n))}catch{n.attempts++,t.push(n)}saveQueue(t)}function startRetryLoop(){retryTimer||(processQueue(),retryTimer=setInterval(processQueue,RETRY_INTERVAL_MS))}function stopRetryLoop(){retryTimer&&(clearInterval(retryTimer),retryTimer=null)}var root_1$8=from_svg('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M15 5L5 15M5 5L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg>'),root_2$8=from_svg('<svg width="22" height="22" viewBox="0 0 24 24" fill="none"><path d="M12 2C6.48 2 2 6.48 2 12C2 13.85 2.5 15.55 3.36 17L2 22L7 20.64C8.45 21.5 10.15 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 10H8.01M12 10H12.01M16 10H16.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg>'),root$6=from_html("<button><!></button>");const $$css$9={hash:"svelte-joatup",code:".jat-fb-btn.svelte-joatup {width:52px;height:52px;border-radius:50%;border:none;background:var(--jat-btn-color, #3b82f6);color:white;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0, 0, 0, 0.25);transition:transform 0.2s, box-shadow 0.2s, background 0.2s;}.jat-fb-btn.svelte-joatup:hover {transform:scale(1.08);box-shadow:0 6px 20px rgba(0, 0, 0, 0.3);}.jat-fb-btn.svelte-joatup:active {transform:scale(0.95);}.jat-fb-btn.open.svelte-joatup {background:#6b7280;}"};function FeedbackButton(e,t){push(t,!0),append_styles(e,$$css$9);let n=prop(t,"onmousedown",7),r=prop(t,"open",7,!1);var o={get onmousedown(){return n()},set onmousedown(u){n(u),flushSync()},get open(){return r()},set open(u=!1){r(u),flushSync()}},s=root$6();let i;var a=child(s);{var l=u=>{var d=root_1$8();append(u,d)},c=u=>{var d=root_2$8();append(u,d)};if_block(a,u=>{r()?u(l):u(c,!1)})}return reset(s),template_effect(()=>{i=set_class(s,1,"jat-fb-btn svelte-joatup",null,i,{open:r()}),set_attribute(s,"aria-label",r()?"Close feedback":"Send feedback"),set_attribute(s,"title",r()?"Close feedback":"Send feedback")}),delegated("mousedown",s,function(...u){var d;(d=n())==null||d.apply(this,u)}),append(e,s),pop(o)}delegate(["mousedown"]),create_custom_element(FeedbackButton,{onmousedown:{},open:{}},[],[],{mode:"open"});const PREFIX="[modern-screenshot]",IN_BROWSER=typeof window<"u",SUPPORT_WEB_WORKER=IN_BROWSER&&"Worker"in window,USER_AGENT=IN_BROWSER?(sr=window.navigator)==null?void 0:sr.userAgent:"",IN_CHROME=USER_AGENT.includes("Chrome"),IN_SAFARI=USER_AGENT.includes("AppleWebKit")&&!IN_CHROME,IN_FIREFOX=USER_AGENT.includes("Firefox"),isContext=e=>e&&"__CONTEXT__"in e,isCssFontFaceRule=e=>e.constructor.name==="CSSFontFaceRule",isCSSImportRule=e=>e.constructor.name==="CSSImportRule",isLayerBlockRule=e=>e.constructor.name==="CSSLayerBlockRule",isElementNode=e=>e.nodeType===1,isSVGElementNode=e=>typeof e.className=="object",isSVGImageElementNode=e=>e.tagName==="image",isSVGUseElementNode=e=>e.tagName==="use",isHTMLElementNode=e=>isElementNode(e)&&typeof e.style<"u"&&!isSVGElementNode(e),isCommentNode=e=>e.nodeType===8,isTextNode=e=>e.nodeType===3,isImageElement=e=>e.tagName==="IMG",isVideoElement=e=>e.tagName==="VIDEO",isCanvasElement=e=>e.tagName==="CANVAS",isTextareaElement=e=>e.tagName==="TEXTAREA",isInputElement=e=>e.tagName==="INPUT",isStyleElement=e=>e.tagName==="STYLE",isScriptElement=e=>e.tagName==="SCRIPT",isSelectElement=e=>e.tagName==="SELECT",isSlotElement=e=>e.tagName==="SLOT",isIFrameElement=e=>e.tagName==="IFRAME",consoleWarn=(...e)=>console.warn(PREFIX,...e);function supportWebp(e){var n;const t=(n=e==null?void 0:e.createElement)==null?void 0:n.call(e,"canvas");return t&&(t.height=t.width=1),!!t&&"toDataURL"in t&&!!t.toDataURL("image/webp").includes("image/webp")}const isDataUrl=e=>e.startsWith("data:");function resolveUrl(e,t){if(e.match(/^[a-z]+:\/\//i))return e;if(IN_BROWSER&&e.match(/^\/\//))return window.location.protocol+e;if(e.match(/^[a-z]+:/i)||!IN_BROWSER)return e;const n=getDocument().implementation.createHTMLDocument(),r=n.createElement("base"),o=n.createElement("a");return n.head.appendChild(r),n.body.appendChild(o),t&&(r.href=t),o.href=e,o.href}function getDocument(e){return(e&&isElementNode(e)?e==null?void 0:e.ownerDocument:e)??window.document}const XMLNS="http://www.w3.org/2000/svg";function createSvg(e,t,n){const r=getDocument(n).createElementNS(XMLNS,"svg");return r.setAttributeNS(null,"width",e.toString()),r.setAttributeNS(null,"height",t.toString()),r.setAttributeNS(null,"viewBox",`0 0 ${e} ${t}`),r}function svgToDataUrl(e,t){let n=new XMLSerializer().serializeToString(e);return t&&(n=n.replace(/[\u0000-\u0008\v\f\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu,"")),`data:image/svg+xml;charset=utf-8,${encodeURIComponent(n)}`}function readBlob(e,t){return new Promise((n,r)=>{const o=new FileReader;o.onload=()=>n(o.result),o.onerror=()=>r(o.error),o.onabort=()=>r(new Error(`Failed read blob to ${t}`)),o.readAsDataURL(e)})}const blobToDataUrl=e=>readBlob(e,"dataUrl");function createImage(e,t){const n=getDocument(t).createElement("img");return n.decoding="sync",n.loading="eager",n.src=e,n}function loadMedia(e,t){return new Promise(n=>{const{timeout:r,ownerDocument:o,onError:s,onWarn:i}=t??{},a=typeof e=="string"?createImage(e,getDocument(o)):e;let l=null,c=null;function u(){n(a),l&&clearTimeout(l),c==null||c()}if(r&&(l=setTimeout(u,r)),isVideoElement(a)){const d=a.currentSrc||a.src;if(!d)return a.poster?loadMedia(a.poster,t).then(n):u();if(a.readyState>=2)return u();const f=u,v=_=>{i==null||i("Failed video load",d,_),s==null||s(_),u()};c=()=>{a.removeEventListener("loadeddata",f),a.removeEventListener("error",v)},a.addEventListener("loadeddata",f,{once:!0}),a.addEventListener("error",v,{once:!0})}else{const d=isSVGImageElementNode(a)?a.href.baseVal:a.currentSrc||a.src;if(!d)return u();const f=async()=>{if(isImageElement(a)&&"decode"in a)try{await a.decode()}catch(_){i==null||i("Failed to decode image, trying to render anyway",a.dataset.originalSrc||d,_)}u()},v=_=>{i==null||i("Failed image load",a.dataset.originalSrc||d,_),u()};if(isImageElement(a)&&a.complete)return f();c=()=>{a.removeEventListener("load",f),a.removeEventListener("error",v)},a.addEventListener("load",f,{once:!0}),a.addEventListener("error",v,{once:!0})}})}async function waitUntilLoad(e,t){isHTMLElementNode(e)&&(isImageElement(e)||isVideoElement(e)?await loadMedia(e,t):await Promise.all(["img","video"].flatMap(n=>Array.from(e.querySelectorAll(n)).map(r=>loadMedia(r,t)))))}const uuid$1=(function(){let t=0;const n=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${n()}${t}`)})();function splitFontFamily(e){return e==null?void 0:e.split(",").map(t=>t.trim().replace(/"|'/g,"").toLowerCase()).filter(Boolean)}let uid$1=0;function createLogger(e){const t=`${PREFIX}[#${uid$1}]`;return uid$1++,{time:n=>e&&console.time(`${t} ${n}`),timeEnd:n=>e&&console.timeEnd(`${t} ${n}`),warn:(...n)=>e&&consoleWarn(...n)}}function getDefaultRequestInit(e){return{cache:e?"no-cache":"force-cache"}}async function orCreateContext(e,t){return isContext(e)?e:createContext(e,{...t,autoDestruct:!0})}async function createContext(e,t){var v,_;const{scale:n=1,workerUrl:r,workerNumber:o=1}=t||{},s=!!(t!=null&&t.debug),i=(t==null?void 0:t.features)??!0,a=e.ownerDocument??(IN_BROWSER?window.document:void 0),l=((v=e.ownerDocument)==null?void 0:v.defaultView)??(IN_BROWSER?window:void 0),c=new Map,u={width:0,height:0,quality:1,type:"image/png",scale:n,backgroundColor:null,style:null,filter:null,maximumCanvasSize:0,timeout:3e4,progress:null,debug:s,fetch:{requestInit:getDefaultRequestInit((_=t==null?void 0:t.fetch)==null?void 0:_.bypassingCache),placeholderImage:"data:image/png;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",bypassingCache:!1,...t==null?void 0:t.fetch},fetchFn:null,font:{},drawImageInterval:100,workerUrl:null,workerNumber:o,onCloneEachNode:null,onCloneNode:null,onEmbedNode:null,onCreateForeignObjectSvg:null,includeStyleProperties:null,autoDestruct:!1,...t,__CONTEXT__:!0,log:createLogger(s),node:e,ownerDocument:a,ownerWindow:l,dpi:n===1?null:96*n,svgStyleElement:createStyleElement(a),svgDefsElement:a==null?void 0:a.createElementNS(XMLNS,"defs"),svgStyles:new Map,defaultComputedStyles:new Map,workers:[...Array.from({length:SUPPORT_WEB_WORKER&&r&&o?o:0})].map(()=>{try{const y=new Worker(r);return y.onmessage=async h=>{var m,E,k,N;const{url:p,result:b}=h.data;b?(E=(m=c.get(p))==null?void 0:m.resolve)==null||E.call(m,b):(N=(k=c.get(p))==null?void 0:k.reject)==null||N.call(k,new Error(`Error receiving message from worker: ${p}`))},y.onmessageerror=h=>{var b,m;const{url:p}=h.data;(m=(b=c.get(p))==null?void 0:b.reject)==null||m.call(b,new Error(`Error receiving message from worker: ${p}`))},y}catch(y){return u.log.warn("Failed to new Worker",y),null}}).filter(Boolean),fontFamilies:new Map,fontCssTexts:new Map,acceptOfImage:`${[supportWebp(a)&&"image/webp","image/svg+xml","image/*","*/*"].filter(Boolean).join(",")};q=0.8`,requests:c,drawImageCount:0,tasks:[],features:i,isEnable:y=>y==="restoreScrollPosition"?typeof i=="boolean"?!1:i[y]??!1:typeof i=="boolean"?i:i[y]??!0,shadowRoots:[]};u.log.time("wait until load"),await waitUntilLoad(e,{timeout:u.timeout,onWarn:u.log.warn}),u.log.timeEnd("wait until load");const{width:d,height:f}=resolveBoundingBox(e,u);return u.width=d,u.height=f,u}function createStyleElement(e){if(!e)return;const t=e.createElement("style"),n=t.ownerDocument.createTextNode(`
32
+ `,document.body.appendChild(e),e}function handleHover(e){if(!isActive||!overlay)return;const t=e.target;if(t===overlay||t.id==="jat-feedback-picker-tooltip")return;const n=t.getBoundingClientRect();overlay.style.top=`${n.top}px`,overlay.style.left=`${n.left}px`,overlay.style.width=`${n.width}px`,overlay.style.height=`${n.height}px`}function handleClick(e){var s;if(!isActive)return;e.preventDefault(),e.stopPropagation();const t=e.target,n=t.getBoundingClientRect(),r=onSelect;stopElementPicker();const o={tagName:t.tagName,className:typeof t.className=="string"?t.className:"",id:t.id,textContent:((s=t.textContent)==null?void 0:s.substring(0,100))||"",attributes:Array.from(t.attributes).reduce((i,a)=>(i[a.name]=a.value,i),{}),xpath:getXPath(t),selector:generateSelector(t),boundingRect:{x:n.x,y:n.y,width:n.width,height:n.height,top:n.top,left:n.left,bottom:n.bottom,right:n.right},screenshot:null,timestamp:new Date().toISOString(),url:window.location.href};r==null||r(o)}function handleEscape(e){e.key==="Escape"&&stopElementPicker()}function startElementPicker(e){isActive||(isActive=!0,onSelect=e,originalCursor=document.body.style.cursor,document.body.style.cursor="crosshair",overlay=createOverlay(),tooltip=showTooltip(),document.addEventListener("mousemove",handleHover,!0),document.addEventListener("click",handleClick,!0),document.addEventListener("keydown",handleEscape,!0))}function stopElementPicker(){isActive&&(isActive=!1,onSelect=null,document.body.style.cursor=originalCursor,overlay&&(overlay.remove(),overlay=null),tooltip&&(tooltip.remove(),tooltip=null),document.removeEventListener("mousemove",handleHover,!0),document.removeEventListener("click",handleClick,!0),document.removeEventListener("keydown",handleEscape,!0))}function isElementPickerActive(){return isActive}const ANNOTATION_COLORS=["#ef4444","#eab308","#22c55e","#3b82f6","#ffffff","#111827"],DEFAULT_STROKE_WIDTH=3;let _editorOpen=!1;function setAnnotationEditorOpen(e){_editorOpen=e}function isAnnotationEditorOpen(){return _editorOpen}let _nextId=1;function nextShapeId(){return _nextId++}function drawArrow(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(n.x,n.y),e.lineTo(r.x,r.y),e.stroke();const i=Math.atan2(r.y-n.y,r.x-n.x),a=14,l=Math.PI/7;e.beginPath(),e.moveTo(r.x,r.y),e.lineTo(r.x-a*Math.cos(i-l),r.y-a*Math.sin(i-l)),e.moveTo(r.x,r.y),e.lineTo(r.x-a*Math.cos(i+l),r.y-a*Math.sin(i+l)),e.stroke()}function drawRectangle(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s,e.lineJoin="round";const i=Math.min(n.x,r.x),a=Math.min(n.y,r.y),l=Math.abs(r.x-n.x),c=Math.abs(r.y-n.y);e.strokeRect(i,a,l,c)}function drawEllipse(e,t){const{start:n,end:r,color:o,strokeWidth:s}=t;e.strokeStyle=o,e.lineWidth=s;const i=(n.x+r.x)/2,a=(n.y+r.y)/2,l=Math.abs(r.x-n.x)/2,c=Math.abs(r.y-n.y)/2;l<1||c<1||(e.beginPath(),e.ellipse(i,a,l,c,0,0,Math.PI*2),e.stroke())}function drawFreehand(e,t){const{points:n,color:r,strokeWidth:o}=t;if(!(n.length<2)){e.strokeStyle=r,e.lineWidth=o,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(n[0].x,n[0].y);for(let s=1;s<n.length;s++)e.lineTo(n[s].x,n[s].y);e.stroke()}}function drawText(e,t){const{position:n,content:r,color:o,fontSize:s}=t;r&&(e.font=`bold ${s}px sans-serif`,e.textBaseline="top",e.strokeStyle="#000000",e.lineWidth=2,e.lineJoin="round",e.strokeText(r,n.x,n.y),e.fillStyle=o,e.fillText(r,n.x,n.y))}function renderShape(e,t){switch(e.save(),t.type){case"arrow":drawArrow(e,t);break;case"rectangle":drawRectangle(e,t);break;case"ellipse":drawEllipse(e,t);break;case"freehand":drawFreehand(e,t);break;case"text":drawText(e,t);break}e.restore()}function renderAllShapes(e,t){for(const n of t)renderShape(e,n)}function mergeAnnotation(e,t,n,r){return new Promise((o,s)=>{const i=new Image;i.onload=()=>{const a=document.createElement("canvas");a.width=n,a.height=r;const l=a.getContext("2d");if(!l){s(new Error("Canvas context unavailable"));return}l.drawImage(i,0,0,n,r),renderAllShapes(l,t),o(a.toDataURL("image/jpeg",.85))},i.onerror=()=>s(new Error("Failed to load image")),i.src=e})}async function submitReport(e,t){const n=`${e.replace(/\/$/,"")}/api/feedback/report`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),o=await r.json();return r.ok?{ok:!0,id:o.id}:{ok:!1,error:o.error||`HTTP ${r.status}`}}async function fetchReports(e){try{const t=`${e.replace(/\/$/,"")}/api/feedback/reports`,n=await fetch(t,{method:"GET",credentials:"same-origin"});if(!n.ok){if(n.status===401||n.status===403)return{reports:[]};const o=await n.json().catch(()=>({error:`HTTP ${n.status}`}));return{reports:[],error:o.error||`HTTP ${n.status}`}}return{reports:(await n.json()).reports||[]}}catch(t){return{reports:[],error:t instanceof Error?t.message:"Failed to fetch"}}}async function respondToReport(e,t,n,r,o){try{const s=`${e.replace(/\/$/,"")}/api/feedback/reports/${t}/respond`,i={response:n};r&&(i.reason=r),o!=null&&o.screenshots&&o.screenshots.length>0&&(i.screenshots=o.screenshots),o!=null&&o.elements&&o.elements.length>0&&(i.elements=o.elements);const a=await fetch(s,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}),l=await a.json();return a.ok?{ok:!0}:{ok:!1,error:l.error||`HTTP ${a.status}`}}catch(s){return{ok:!1,error:s instanceof Error?s.message:"Failed to respond"}}}const notesUrl=e=>`${e.replace(/\/$/,"")}/api/feedback/notes`;async function fetchNotes(e,t,n){try{let r=`${notesUrl(e)}?project=${encodeURIComponent(t)}`;const o=await fetch(r,{credentials:"same-origin"});if(!o.ok){const i=await o.json().catch(()=>({error:`HTTP ${o.status}`}));return{notes:[],error:i.error||`HTTP ${o.status}`}}return{notes:(await o.json()).notes||[]}}catch(r){return{notes:[],error:r instanceof Error?r.message:"Failed to fetch notes"}}}async function createNote(e,t){try{const n=await fetch(notesUrl(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json();return n.ok?{ok:!0,note:r.note}:{ok:!1,error:r.error||`HTTP ${n.status}`}}catch(n){return{ok:!1,error:n instanceof Error?n.message:"Failed to create note"}}}async function updateNote(e,t,n){try{const r=await fetch(`${notesUrl(e)}/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}),o=await r.json();return r.ok?{ok:!0}:{ok:!1,error:o.error||`HTTP ${r.status}`}}catch(r){return{ok:!1,error:r instanceof Error?r.message:"Failed to update note"}}}async function deleteNote(e,t){try{const n=await fetch(`${notesUrl(e)}/${t}`,{method:"DELETE"}),r=await n.json();return n.ok?{ok:!0}:{ok:!1,error:r.error||`HTTP ${n.status}`}}catch(n){return{ok:!1,error:n instanceof Error?n.message:"Failed to delete note"}}}const STORAGE_KEY="jat-feedback-queue",MAX_ENTRIES=50,RETRY_INTERVAL_MS=3e4;let retryTimer=null;function getQueue(){try{const e=localStorage.getItem(STORAGE_KEY);return e?JSON.parse(e):[]}catch{return[]}}function saveQueue(e){try{localStorage.setItem(STORAGE_KEY,JSON.stringify(e))}catch{}}function enqueue(e,t){const n=getQueue();for(n.push({report:t,endpoint:e,queuedAt:new Date().toISOString(),attempts:0});n.length>MAX_ENTRIES;)n.shift();saveQueue(n)}async function processQueue(){const e=getQueue();if(e.length===0)return;const t=[];for(const n of e)try{(await submitReport(n.endpoint,n.report)).ok||(n.attempts++,t.push(n))}catch{n.attempts++,t.push(n)}saveQueue(t)}function startRetryLoop(){retryTimer||(processQueue(),retryTimer=setInterval(processQueue,RETRY_INTERVAL_MS))}function stopRetryLoop(){retryTimer&&(clearInterval(retryTimer),retryTimer=null)}var root_1$8=from_svg('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M15 5L5 15M5 5L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg>'),root_2$8=from_svg('<svg width="22" height="22" viewBox="0 0 24 24" fill="none"><path d="M12 2C6.48 2 2 6.48 2 12C2 13.85 2.5 15.55 3.36 17L2 22L7 20.64C8.45 21.5 10.15 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 10H8.01M12 10H12.01M16 10H16.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg>'),root$6=from_html("<button><!></button>");const $$css$9={hash:"svelte-joatup",code:".jat-fb-btn.svelte-joatup {width:52px;height:52px;border-radius:50%;border:none;background:var(--jat-btn-color, #3b82f6);color:white;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0, 0, 0, 0.25);transition:transform 0.2s, box-shadow 0.2s, background 0.2s;}.jat-fb-btn.svelte-joatup:hover {transform:scale(1.08);box-shadow:0 6px 20px rgba(0, 0, 0, 0.3);}.jat-fb-btn.svelte-joatup:active {transform:scale(0.95);}.jat-fb-btn.open.svelte-joatup {background:#6b7280;}"};function FeedbackButton(e,t){push(t,!0),append_styles(e,$$css$9);let n=prop(t,"onmousedown",7),r=prop(t,"open",7,!1);var o={get onmousedown(){return n()},set onmousedown(u){n(u),flushSync()},get open(){return r()},set open(u=!1){r(u),flushSync()}},s=root$6();let i;var a=child(s);{var l=u=>{var d=root_1$8();append(u,d)},c=u=>{var d=root_2$8();append(u,d)};if_block(a,u=>{r()?u(l):u(c,!1)})}return reset(s),template_effect(()=>{i=set_class(s,1,"jat-fb-btn svelte-joatup",null,i,{open:r()}),set_attribute(s,"aria-label",r()?"Close feedback":"Send feedback"),set_attribute(s,"title",r()?"Close feedback":"Send feedback")}),delegated("mousedown",s,function(...u){var d;(d=n())==null||d.apply(this,u)}),append(e,s),pop(o)}delegate(["mousedown"]),create_custom_element(FeedbackButton,{onmousedown:{},open:{}},[],[],{mode:"open"});const PREFIX="[modern-screenshot]",IN_BROWSER=typeof window<"u",SUPPORT_WEB_WORKER=IN_BROWSER&&"Worker"in window,USER_AGENT=IN_BROWSER?(sr=window.navigator)==null?void 0:sr.userAgent:"",IN_CHROME=USER_AGENT.includes("Chrome"),IN_SAFARI=USER_AGENT.includes("AppleWebKit")&&!IN_CHROME,IN_FIREFOX=USER_AGENT.includes("Firefox"),isContext=e=>e&&"__CONTEXT__"in e,isCssFontFaceRule=e=>e.constructor.name==="CSSFontFaceRule",isCSSImportRule=e=>e.constructor.name==="CSSImportRule",isLayerBlockRule=e=>e.constructor.name==="CSSLayerBlockRule",isElementNode=e=>e.nodeType===1,isSVGElementNode=e=>typeof e.className=="object",isSVGImageElementNode=e=>e.tagName==="image",isSVGUseElementNode=e=>e.tagName==="use",isHTMLElementNode=e=>isElementNode(e)&&typeof e.style<"u"&&!isSVGElementNode(e),isCommentNode=e=>e.nodeType===8,isTextNode=e=>e.nodeType===3,isImageElement=e=>e.tagName==="IMG",isVideoElement=e=>e.tagName==="VIDEO",isCanvasElement=e=>e.tagName==="CANVAS",isTextareaElement=e=>e.tagName==="TEXTAREA",isInputElement=e=>e.tagName==="INPUT",isStyleElement=e=>e.tagName==="STYLE",isScriptElement=e=>e.tagName==="SCRIPT",isSelectElement=e=>e.tagName==="SELECT",isSlotElement=e=>e.tagName==="SLOT",isIFrameElement=e=>e.tagName==="IFRAME",consoleWarn=(...e)=>console.warn(PREFIX,...e);function supportWebp(e){var n;const t=(n=e==null?void 0:e.createElement)==null?void 0:n.call(e,"canvas");return t&&(t.height=t.width=1),!!t&&"toDataURL"in t&&!!t.toDataURL("image/webp").includes("image/webp")}const isDataUrl=e=>e.startsWith("data:");function resolveUrl(e,t){if(e.match(/^[a-z]+:\/\//i))return e;if(IN_BROWSER&&e.match(/^\/\//))return window.location.protocol+e;if(e.match(/^[a-z]+:/i)||!IN_BROWSER)return e;const n=getDocument().implementation.createHTMLDocument(),r=n.createElement("base"),o=n.createElement("a");return n.head.appendChild(r),n.body.appendChild(o),t&&(r.href=t),o.href=e,o.href}function getDocument(e){return(e&&isElementNode(e)?e==null?void 0:e.ownerDocument:e)??window.document}const XMLNS="http://www.w3.org/2000/svg";function createSvg(e,t,n){const r=getDocument(n).createElementNS(XMLNS,"svg");return r.setAttributeNS(null,"width",e.toString()),r.setAttributeNS(null,"height",t.toString()),r.setAttributeNS(null,"viewBox",`0 0 ${e} ${t}`),r}function svgToDataUrl(e,t){let n=new XMLSerializer().serializeToString(e);return t&&(n=n.replace(/[\u0000-\u0008\v\f\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu,"")),`data:image/svg+xml;charset=utf-8,${encodeURIComponent(n)}`}function readBlob(e,t){return new Promise((n,r)=>{const o=new FileReader;o.onload=()=>n(o.result),o.onerror=()=>r(o.error),o.onabort=()=>r(new Error(`Failed read blob to ${t}`)),o.readAsDataURL(e)})}const blobToDataUrl=e=>readBlob(e,"dataUrl");function createImage(e,t){const n=getDocument(t).createElement("img");return n.decoding="sync",n.loading="eager",n.src=e,n}function loadMedia(e,t){return new Promise(n=>{const{timeout:r,ownerDocument:o,onError:s,onWarn:i}=t??{},a=typeof e=="string"?createImage(e,getDocument(o)):e;let l=null,c=null;function u(){n(a),l&&clearTimeout(l),c==null||c()}if(r&&(l=setTimeout(u,r)),isVideoElement(a)){const d=a.currentSrc||a.src;if(!d)return a.poster?loadMedia(a.poster,t).then(n):u();if(a.readyState>=2)return u();const f=u,v=_=>{i==null||i("Failed video load",d,_),s==null||s(_),u()};c=()=>{a.removeEventListener("loadeddata",f),a.removeEventListener("error",v)},a.addEventListener("loadeddata",f,{once:!0}),a.addEventListener("error",v,{once:!0})}else{const d=isSVGImageElementNode(a)?a.href.baseVal:a.currentSrc||a.src;if(!d)return u();const f=async()=>{if(isImageElement(a)&&"decode"in a)try{await a.decode()}catch(_){i==null||i("Failed to decode image, trying to render anyway",a.dataset.originalSrc||d,_)}u()},v=_=>{i==null||i("Failed image load",a.dataset.originalSrc||d,_),u()};if(isImageElement(a)&&a.complete)return f();c=()=>{a.removeEventListener("load",f),a.removeEventListener("error",v)},a.addEventListener("load",f,{once:!0}),a.addEventListener("error",v,{once:!0})}})}async function waitUntilLoad(e,t){isHTMLElementNode(e)&&(isImageElement(e)||isVideoElement(e)?await loadMedia(e,t):await Promise.all(["img","video"].flatMap(n=>Array.from(e.querySelectorAll(n)).map(r=>loadMedia(r,t)))))}const uuid$1=(function(){let t=0;const n=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${n()}${t}`)})();function splitFontFamily(e){return e==null?void 0:e.split(",").map(t=>t.trim().replace(/"|'/g,"").toLowerCase()).filter(Boolean)}let uid$1=0;function createLogger(e){const t=`${PREFIX}[#${uid$1}]`;return uid$1++,{time:n=>e&&console.time(`${t} ${n}`),timeEnd:n=>e&&console.timeEnd(`${t} ${n}`),warn:(...n)=>e&&consoleWarn(...n)}}function getDefaultRequestInit(e){return{cache:e?"no-cache":"force-cache"}}async function orCreateContext(e,t){return isContext(e)?e:createContext(e,{...t,autoDestruct:!0})}async function createContext(e,t){var v,_;const{scale:n=1,workerUrl:r,workerNumber:o=1}=t||{},s=!!(t!=null&&t.debug),i=(t==null?void 0:t.features)??!0,a=e.ownerDocument??(IN_BROWSER?window.document:void 0),l=((v=e.ownerDocument)==null?void 0:v.defaultView)??(IN_BROWSER?window:void 0),c=new Map,u={width:0,height:0,quality:1,type:"image/png",scale:n,backgroundColor:null,style:null,filter:null,maximumCanvasSize:0,timeout:3e4,progress:null,debug:s,fetch:{requestInit:getDefaultRequestInit((_=t==null?void 0:t.fetch)==null?void 0:_.bypassingCache),placeholderImage:"data:image/png;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",bypassingCache:!1,...t==null?void 0:t.fetch},fetchFn:null,font:{},drawImageInterval:100,workerUrl:null,workerNumber:o,onCloneEachNode:null,onCloneNode:null,onEmbedNode:null,onCreateForeignObjectSvg:null,includeStyleProperties:null,autoDestruct:!1,...t,__CONTEXT__:!0,log:createLogger(s),node:e,ownerDocument:a,ownerWindow:l,dpi:n===1?null:96*n,svgStyleElement:createStyleElement(a),svgDefsElement:a==null?void 0:a.createElementNS(XMLNS,"defs"),svgStyles:new Map,defaultComputedStyles:new Map,workers:[...Array.from({length:SUPPORT_WEB_WORKER&&r&&o?o:0})].map(()=>{try{const y=new Worker(r);return y.onmessage=async h=>{var m,E,k,N;const{url:p,result:b}=h.data;b?(E=(m=c.get(p))==null?void 0:m.resolve)==null||E.call(m,b):(N=(k=c.get(p))==null?void 0:k.reject)==null||N.call(k,new Error(`Error receiving message from worker: ${p}`))},y.onmessageerror=h=>{var b,m;const{url:p}=h.data;(m=(b=c.get(p))==null?void 0:b.reject)==null||m.call(b,new Error(`Error receiving message from worker: ${p}`))},y}catch(y){return u.log.warn("Failed to new Worker",y),null}}).filter(Boolean),fontFamilies:new Map,fontCssTexts:new Map,acceptOfImage:`${[supportWebp(a)&&"image/webp","image/svg+xml","image/*","*/*"].filter(Boolean).join(",")};q=0.8`,requests:c,drawImageCount:0,tasks:[],features:i,isEnable:y=>y==="restoreScrollPosition"?typeof i=="boolean"?!1:i[y]??!1:typeof i=="boolean"?i:i[y]??!0,shadowRoots:[]};u.log.time("wait until load"),await waitUntilLoad(e,{timeout:u.timeout,onWarn:u.log.warn}),u.log.timeEnd("wait until load");const{width:d,height:f}=resolveBoundingBox(e,u);return u.width=d,u.height=f,u}function createStyleElement(e){if(!e)return;const t=e.createElement("style"),n=t.ownerDocument.createTextNode(`
33
33
  .______background-clip--text {
34
34
  background-clip: text;
35
35
  -webkit-background-clip: text;
@@ -375,7 +375,7 @@ Parameters: ${o}`,inputSchema:record(string(),any()),async execute(s){try{const
375
375
  animation: svelte-nv4d5v-spin 0.6s linear infinite;}
376
376
  @keyframes svelte-nv4d5v-spin {
377
377
  to { transform: rotate(360deg); }
378
- }.requests-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.agent-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.notes-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.panel-version.svelte-nv4d5v {font-size:10px;color:#4b5563;margin-right:auto;align-self:flex-end;padding-bottom:6px;}`};function FeedbackPanel(e,t){push(t,!0),append_styles(e,$$css$1);const n="3.0.0";let r=prop(t,"endpoint",7),o=prop(t,"project",7),s=prop(t,"isOpen",7,!1),i=prop(t,"userId",7,""),a=prop(t,"userEmail",7,""),l=prop(t,"userName",7,""),c=prop(t,"userRole",7,""),u=prop(t,"orgId",7,""),d=prop(t,"orgName",7,""),f=prop(t,"onclose",7),v=prop(t,"ongrip",7),_=prop(t,"agentProxy",7,""),y=prop(t,"agentModel",7,""),h=prop(t,"agentContext",7,""),p=prop(t,"registeredTools",23,()=>[]),b=state("new"),m=state(!1),E=state(!1),k=state(proxy([])),N=state("idle"),P=state(0),Z=state(!1),Q=state(null);function re(){return get(Q)||set(Q,new AgentBridge({proxyUrl:_(),model:y()||void 0,maxSteps:20,appContext:h()||void 0,endpoint:r(),project:o(),registeredTools:p(),onMessagesChange:T=>{set(k,T,!0)},onStateChange:(T,D)=>{set(N,T,!0),set(P,D,!0)}}),!0),get(Q)}function ce(){var T;(T=get(Q))==null||T.invalidateNotesCache()}user_effect(()=>{get(b)==="agent"&&!get(m)&&set(m,!0)}),user_effect(()=>{get(b)==="notes"&&!get(E)&&set(E,!0)});function ie(T){re().execute(T)}function we(){var T;(T=get(Q))==null||T.stop()}function ge(T){var D;(D=get(Q))==null||D.approve(T)}function ze(T){var D;(D=get(Q))==null||D.skip(T)}function xe(T){set(Z,T,!0),get(Q)&&(get(Q).autoApprove=T)}onDestroy(()=>{var T;(T=get(Q))==null||T.dispose()});let ve=state(proxy([])),me=state(!1),g=state(""),x=user_derived(()=>get(ve).filter(T=>T.status==="completed").length);async function S(){set(me,!0),set(g,"");const T=await fetchReports(r());set(ve,T.reports,!0),T.error&&set(g,T.error,!0),set(me,!1)}user_effect(()=>{r()&&S()});let A=state(""),O=state(""),C=state("bug"),V=state("medium"),U=state(proxy([])),j=state(proxy([])),W=state(proxy([])),K=state(proxy([])),oe=state(void 0);const R=["image/png","image/jpeg","image/gif","image/webp","image/svg+xml"];function M(){var T;(T=get(oe))==null||T.click()}async function G(T){const D=T.target,se=D.files;if(!(!se||se.length===0)){for(const te of se)try{const Oe=await ee(te);R.includes(te.type)?(set(U,[...get(U),Oe],!0),q(`Image added: ${te.name}`,"success")):(set(j,[...get(j),{name:te.name,type:te.type||"application/octet-stream",data:Oe,size:te.size}],!0),q(`File attached: ${te.name}`,"success"))}catch{q(`Failed to read: ${te.name}`,"error")}D.value=""}}function ee(T){return new Promise((D,se)=>{const te=new FileReader;te.onload=()=>D(te.result),te.onerror=()=>se(te.error),te.readAsDataURL(T)})}function ue(T){set(j,get(j).filter((D,se)=>se!==T),!0)}function _e(T){return T<1024?`${T}B`:T<1024*1024?`${(T/1024).toFixed(1)}KB`:`${(T/(1024*1024)).toFixed(1)}MB`}let Y=state(!1),Te=state(!1),Ie=state(!1),z=state(null),w=state(""),I=state(void 0),L=!1;user_effect(()=>{s()&&!L&&(requestAnimationFrame(()=>{requestAnimationFrame(()=>{var T;(T=get(I))==null||T.focus()})}),get(b)==="new"&&get(U).length===0&&setTimeout(()=>{captureViewportQuick().then(T=>{set(U,[...get(U),T],!0)}).catch(()=>{})},300)),L=s()});let X=state(""),F=state("success"),H=state(!1);function q(T,D){set(X,T,!0),set(F,D,!0),set(H,!0),setTimeout(()=>{set(H,!1)},3e3)}async function ke(){set(Te,!0);try{const T=await captureViewport();set(w,T,!0),set(z,get(U).length,!0)}catch(T){console.error("[jat-feedback] Screenshot failed:",T),q("Screenshot failed: "+(T instanceof Error?T.message:"unknown error"),"error")}finally{set(Te,!1)}}function ae(T){set(U,get(U).filter((D,se)=>se!==T),!0)}function pe(T){set(w,get(U)[T],!0),set(z,T,!0)}function Ee(T){get(z)!==null&&(get(z)>=get(U).length?(set(U,[...get(U),T],!0),q(`Screenshot captured (${get(U).length})`,"success")):(set(U,get(U).map((D,se)=>se===get(z)?T:D),!0),q("Screenshot updated","success"))),set(z,null),set(w,"")}function B(){get(z)!==null&&get(z)>=get(U).length&&(set(U,[...get(U),get(w)],!0),q(`Screenshot captured (${get(U).length})`,"success")),set(z,null),set(w,"")}function Pe(){set(Ie,!0),startElementPicker(T=>{set(W,[...get(W),T],!0),set(Ie,!1),q(`Element captured: <${T.tagName.toLowerCase()}>`,"success")})}function nt(){set(K,getCapturedLogs(),!0)}async function Ne(T){if(T.preventDefault(),!get(A).trim())return;set(Y,!0),nt();const D={};(i()||a()||l()||c())&&(D.reporter={},i()&&(D.reporter.userId=i()),a()&&(D.reporter.email=a()),l()&&(D.reporter.name=l()),c()&&(D.reporter.role=c())),(u()||d())&&(D.organization={},u()&&(D.organization.id=u()),d()&&(D.organization.name=d()));const se={title:get(A).trim(),description:get(O).trim(),type:get(C),priority:get(V),project:o()||"",page_url:window.location.href,user_agent:navigator.userAgent,console_logs:get(K).length>0?get(K):null,selected_elements:get(W).length>0?get(W):null,screenshots:get(U).length>0?get(U):null,attachments:get(j).length>0?get(j):null,metadata:Object.keys(D).length>0?D:null};try{const te=await submitReport(r(),se);te.ok?(q(`Report submitted (${te.id})`,"success"),je(),setTimeout(()=>{S(),set(b,"requests")},1200)):(enqueue(r(),se),q("Queued for retry (endpoint unreachable)","error"))}catch{enqueue(r(),se),q("Queued for retry (endpoint unreachable)","error")}finally{set(Y,!1)}}function je(){set(A,""),set(O,""),set(C,"bug"),set(V,"medium"),set(U,[],!0),set(j,[],!0),set(W,[],!0),set(K,[],!0)}user_effect(()=>{nt()});function rt(T){T.stopPropagation()}const Zt=[{value:"bug",label:"Bug"},{value:"enhancement",label:"Enhancement"},{value:"other",label:"Other"}],bn=[{value:"low",label:"Low"},{value:"medium",label:"Medium"},{value:"high",label:"High"},{value:"critical",label:"Critical"}];function be(){return get(U).length+get(j).length+get(W).length}var Ae={get endpoint(){return r()},set endpoint(T){r(T),flushSync()},get project(){return o()},set project(T){o(T),flushSync()},get isOpen(){return s()},set isOpen(T=!1){s(T),flushSync()},get userId(){return i()},set userId(T=""){i(T),flushSync()},get userEmail(){return a()},set userEmail(T=""){a(T),flushSync()},get userName(){return l()},set userName(T=""){l(T),flushSync()},get userRole(){return c()},set userRole(T=""){c(T),flushSync()},get orgId(){return u()},set orgId(T=""){u(T),flushSync()},get orgName(){return d()},set orgName(T=""){d(T),flushSync()},get onclose(){return f()},set onclose(T){f(T),flushSync()},get ongrip(){return v()},set ongrip(T){v(T),flushSync()},get agentProxy(){return _()},set agentProxy(T=""){_(T),flushSync()},get agentModel(){return y()},set agentModel(T=""){y(T),flushSync()},get agentContext(){return h()},set agentContext(T=""){h(T),flushSync()},get registeredTools(){return p()},set registeredTools(T=[]){p(T),flushSync()}},Xe=root$1(),ot=first_child(Xe),dt=child(ot),Rn=child(dt);{var nn=T=>{var D=root_1$1();delegated("mousedown",D,function(...se){var te;(te=v())==null||te.apply(this,se)}),append(T,D)};if_block(Rn,T=>{v()&&T(nn)})}var $t=sibling(Rn,2),Wt=child($t);let dn;var Tt=sibling(Wt,2);let In;var Nn=sibling(child(Tt),2);{var Bn=T=>{var D=root_2$1(),se=child(D,!0);reset(D),template_effect(()=>set_text(se,get(x))),append(T,D)};if_block(Nn,T=>{get(x)>0&&T(Bn)})}reset(Tt);var On=sibling(Tt,2);{var Ln=T=>{var D=root_3();let se;template_effect(()=>se=set_class(D,1,"tab svelte-nv4d5v",null,se,{active:get(b)==="agent"})),delegated("click",D,()=>{set(b,"agent"),set(m,!0)}),append(T,D)};if_block(On,T=>{_()&&T(Ln)})}var Pn=sibling(On,2);let yn;reset($t);var Vn=sibling($t,2);reset(dt);var wn=sibling(dt,2);{var xn=T=>{var D=root_4(),se=child(D),te=sibling(child(se),2);remove_input_defaults(te),bind_this(te,J=>set(I,J),()=>get(I)),reset(se);var Oe=sibling(se,2),Se=sibling(child(Oe),2);remove_textarea_child(Se),reset(Oe);var Je=sibling(Oe,2),jt=child(Je),$e=sibling(child(jt),2);each($e,21,()=>Zt,index,(J,de)=>{var Le=root_5(),Ue=child(Le,!0);reset(Le);var Be={};template_effect(()=>{set_text(Ue,get(de).label),Be!==(Be=get(de).value)&&(Le.value=(Le.__value=get(de).value)??"")}),append(J,Le)}),reset($e),reset(jt);var Ut=sibling(jt,2),St=sibling(child(Ut),2);each(St,21,()=>bn,index,(J,de)=>{var Le=root_6(),Ue=child(Le,!0);reset(Le);var Be={};template_effect(()=>{set_text(Ue,get(de).label),Be!==(Be=get(de).value)&&(Le.value=(Le.__value=get(de).value)??"")}),append(J,Le)}),reset(St),reset(Ut),reset(Je);var Bt=sibling(Je,2),ft=child(Bt),Ct=child(ft),pt=child(Ct);{var fn=J=>{var de=root_7();next(),append(J,de)},kn=J=>{var de=root_8(),Le=sibling(first_child(de),2);{var Ue=Be=>{var Ge=root_9(),mt=child(Ge,!0);reset(Ge),template_effect(()=>set_text(mt,get(U).length)),append(Be,Ge)};if_block(Le,Be=>{get(U).length>0&&Be(Ue)})}append(J,de)};if_block(pt,J=>{get(Te)?J(fn):J(kn,!1)})}reset(Ct);var zt=sibling(Ct,2),En=sibling(child(zt),2);{var Fe=J=>{var de=text("Click an element...");append(J,de)},qe=J=>{var de=root_11(),Le=sibling(first_child(de));{var Ue=Be=>{var Ge=root_12(),mt=child(Ge,!0);reset(Ge),template_effect(()=>set_text(mt,get(W).length)),append(Be,Ge)};if_block(Le,Be=>{get(W).length>0&&Be(Ue)})}append(J,de)};if_block(En,J=>{get(Ie)?J(Fe):J(qe,!1)})}reset(zt);var Qe=sibling(zt,2),ht=sibling(child(Qe),2);{var Vt=J=>{var de=root_13(),Le=child(de,!0);reset(de),template_effect(()=>set_text(Le,get(j).length)),append(J,de)};if_block(ht,J=>{get(j).length>0&&J(Vt)})}reset(Qe);var ct=sibling(Qe,2);bind_this(ct,J=>set(oe,J),()=>get(oe)),reset(ft);var At=sibling(ft,2);ScreenshotPreview(At,{get screenshots(){return get(U)},get capturing(){return get(Te)},oncapture:ke,onremove:ae,onedit:pe}),reset(Bt);var Gt=sibling(Bt,2);{var He=J=>{var de=root_14();each(de,21,()=>get(j),index,(Le,Ue,Be)=>{var Ge=root_15(),mt=child(Ge),Wn=child(mt);{var Tn=bt=>{var Cn=root_16();append(bt,Cn)},Gn=user_derived(()=>get(Ue).type.includes("pdf")),Yn=bt=>{var Cn=root_17();append(bt,Cn)},rn=user_derived(()=>get(Ue).type.includes("markdown")||get(Ue).name.endsWith(".md")),Sn=bt=>{var Cn=root_18();append(bt,Cn)};if_block(Wn,bt=>{get(Gn)?bt(Tn):get(rn)?bt(Yn,1):bt(Sn,!1)})}reset(mt);var Xn=sibling(mt,2),dr=child(Xn,!0);reset(Xn);var Kn=sibling(Xn,2),fr=child(Kn,!0);reset(Kn);var pr=sibling(Kn,2);reset(Ge),template_effect(bt=>{set_text(dr,get(Ue).name),set_text(fr,bt)},[()=>_e(get(Ue).size)]),delegated("click",pr,()=>ue(Be)),append(Le,Ge)}),reset(de),append(J,de)};if_block(Gt,J=>{get(j).length>0&&J(He)})}var We=sibling(Gt,2);{var Ht=J=>{var de=root_19();each(de,21,()=>get(W),index,(Le,Ue,Be)=>{var Ge=root_20(),mt=child(Ge),Wn=child(mt);reset(mt);var Tn=sibling(mt,2),Gn=child(Tn,!0);reset(Tn);var Yn=sibling(Tn,2);reset(Ge),template_effect((rn,Sn)=>{set_text(Wn,`<${rn??""}>`),set_text(Gn,Sn)},[()=>get(Ue).tagName.toLowerCase(),()=>{var rn;return((rn=get(Ue).textContent)==null?void 0:rn.substring(0,40))||get(Ue).selector}]),delegated("click",Yn,()=>{set(W,get(W).filter((rn,Sn)=>Sn!==Be),!0)}),append(Le,Ge)}),reset(de),append(J,de)};if_block(We,J=>{get(W).length>0&&J(Ht)})}var st=sibling(We,2);ConsoleLogList(st,{get logs(){return get(K)}});var it=sibling(st,2);{var gt=J=>{var de=root_21(),Le=child(de);reset(de),template_effect((Ue,Be)=>set_text(Le,`${Ue??""} attachment${Be??""} will be included`),[be,()=>be()>1?"s":""]),append(J,de)},Rt=user_derived(()=>be()>0);if_block(it,J=>{get(Rt)&&J(gt)})}var It=sibling(it,2),Yt=child(It),$n=child(Yt);reset(Yt);var vt=sibling(Yt,2),_t=sibling(vt,2),Nt=child(_t);{var pn=J=>{var de=root_22();next(),append(J,de)},ur=J=>{var de=text("Submit");append(J,de)};if_block(Nt,J=>{get(Y)?J(pn):J(ur,!1)})}reset(_t),reset(It),reset(D),template_effect(J=>{te.disabled=get(Y),Se.disabled=get(Y),$e.disabled=get(Y),St.disabled=get(Y),Ct.disabled=get(Te),zt.disabled=get(Ie),Qe.disabled=get(Y),set_text($n,`v${n}`),vt.disabled=get(Y),_t.disabled=J},[()=>get(Y)||!get(A).trim()]),event("submit",D,Ne),bind_value(te,()=>get(A),J=>set(A,J)),bind_value(Se,()=>get(O),J=>set(O,J)),bind_select_value($e,()=>get(C),J=>set(C,J)),bind_select_value(St,()=>get(V),J=>set(V,J)),delegated("click",Ct,ke),delegated("click",zt,Pe),delegated("click",Qe,M),delegated("change",ct,G),delegated("click",vt,function(...J){var de;(de=f())==null||de.apply(this,J)}),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(wn,T=>{get(b)==="new"&&T(xn)})}var Mn=sibling(wn,2);{var Hn=T=>{var D=root_24(),se=child(D);RequestList(se,{get endpoint(){return r()},get loading(){return get(me)},get error(){return get(g)},onreload:S,get reports(){return get(ve)},set reports(te){set(ve,te,!0)}}),reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Mn,T=>{get(b)==="requests"&&T(Hn)})}var Fn=sibling(Mn,2);{var qn=T=>{var D=root_25(),se=child(D);{let te=user_derived(()=>{var Oe;return((Oe=get(Q))==null?void 0:Oe.getMaxSteps())??20});AgentPanel(se,{get messages(){return get(k)},get agentState(){return get(N)},get currentStep(){return get(P)},get maxSteps(){return get(te)},get autoApprove(){return get(Z)},onsend:ie,onstop:we,onapprove:ge,onskip:ze,onautoapprovechange:xe})}reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Fn,T=>{get(b)==="agent"&&get(m)&&T(qn)})}var Dn=sibling(Fn,2);{var he=T=>{var D=root_26(),se=child(D);NotesPanel(se,{get endpoint(){return r()},get project(){return o()},onnoteschanged:ce}),reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Dn,T=>{get(b)==="notes"&&get(E)&&T(he)})}var ye=sibling(Dn,2);StatusToast(ye,{get message(){return get(X)},get type(){return get(F)},get visible(){return get(H)}}),reset(ot);var Me=sibling(ot,2);{var Ke=T=>{AnnotationEditor(T,{get imageDataUrl(){return get(w)},onsave:Ee,oncancel:B})};if_block(Me,T=>{get(z)!==null&&T(Ke)})}return template_effect(()=>{dn=set_class(Wt,1,"tab svelte-nv4d5v",null,dn,{active:get(b)==="new"}),In=set_class(Tt,1,"tab svelte-nv4d5v",null,In,{active:get(b)==="requests"}),yn=set_class(Pn,1,"tab svelte-nv4d5v",null,yn,{active:get(b)==="notes"})}),delegated("keydown",ot,rt),delegated("keyup",ot,rt),event("keypress",ot,rt),delegated("click",Wt,()=>set(b,"new")),delegated("click",Tt,()=>set(b,"requests")),delegated("click",Pn,()=>{set(b,"notes"),set(E,!0)}),delegated("click",Vn,function(...T){var D;(D=f())==null||D.apply(this,T)}),append(e,Xe),pop(Ae)}delegate(["keydown","keyup","mousedown","click","change"]),create_custom_element(FeedbackPanel,{endpoint:{},project:{},isOpen:{},userId:{},userEmail:{},userName:{},userRole:{},orgId:{},orgName:{},onclose:{},ongrip:{},agentProxy:{},agentModel:{},agentContext:{},registeredTools:{}},[],[],{mode:"open"});var root_1=from_html("<div><!></div>"),root_2=from_html('<div class="jat-feedback-panel svelte-qpyrvv"><div class="no-endpoint svelte-qpyrvv"><p class="svelte-qpyrvv">No endpoint configured.</p> <p class="svelte-qpyrvv">Add the <code class="svelte-qpyrvv">endpoint</code> attribute:</p> <code class="example svelte-qpyrvv">&lt;jat-feedback endpoint="http://localhost:3333"&gt;</code></div></div>'),root=from_html('<div class="jat-feedback-root svelte-qpyrvv" data-page-agent-not-interactive=""><!> <!></div>');const $$css={hash:"svelte-qpyrvv",code:`.jat-feedback-root.svelte-qpyrvv {position:fixed;z-index:2147483647;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;}.jat-feedback-panel.svelte-qpyrvv {position:absolute;
378
+ }.requests-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.agent-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.notes-wrapper.svelte-nv4d5v {flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}.panel-version.svelte-nv4d5v {font-size:10px;color:#4b5563;margin-right:auto;align-self:flex-end;padding-bottom:6px;}`};function FeedbackPanel(e,t){push(t,!0),append_styles(e,$$css$1);const n="3.0.1";let r=prop(t,"endpoint",7),o=prop(t,"project",7),s=prop(t,"isOpen",7,!1),i=prop(t,"userId",7,""),a=prop(t,"userEmail",7,""),l=prop(t,"userName",7,""),c=prop(t,"userRole",7,""),u=prop(t,"orgId",7,""),d=prop(t,"orgName",7,""),f=prop(t,"onclose",7),v=prop(t,"ongrip",7),_=prop(t,"agentProxy",7,""),y=prop(t,"agentModel",7,""),h=prop(t,"agentContext",7,""),p=prop(t,"registeredTools",23,()=>[]),b=state("new"),m=state(!1),E=state(!1),k=state(proxy([])),N=state("idle"),P=state(0),Z=state(!1),Q=state(null);function re(){return get(Q)||set(Q,new AgentBridge({proxyUrl:_(),model:y()||void 0,maxSteps:20,appContext:h()||void 0,endpoint:r(),project:o(),registeredTools:p(),onMessagesChange:T=>{set(k,T,!0)},onStateChange:(T,D)=>{set(N,T,!0),set(P,D,!0)}}),!0),get(Q)}function ce(){var T;(T=get(Q))==null||T.invalidateNotesCache()}user_effect(()=>{get(b)==="agent"&&!get(m)&&set(m,!0)}),user_effect(()=>{get(b)==="notes"&&!get(E)&&set(E,!0)});function ie(T){re().execute(T)}function we(){var T;(T=get(Q))==null||T.stop()}function ge(T){var D;(D=get(Q))==null||D.approve(T)}function ze(T){var D;(D=get(Q))==null||D.skip(T)}function xe(T){set(Z,T,!0),get(Q)&&(get(Q).autoApprove=T)}onDestroy(()=>{var T;(T=get(Q))==null||T.dispose()});let ve=state(proxy([])),me=state(!1),g=state(""),x=user_derived(()=>get(ve).filter(T=>T.status==="completed").length);async function S(){set(me,!0),set(g,"");const T=await fetchReports(r());set(ve,T.reports,!0),T.error&&set(g,T.error,!0),set(me,!1)}user_effect(()=>{r()&&S()});let A=state(""),O=state(""),C=state("bug"),V=state("medium"),U=state(proxy([])),j=state(proxy([])),W=state(proxy([])),K=state(proxy([])),oe=state(void 0);const R=["image/png","image/jpeg","image/gif","image/webp","image/svg+xml"];function M(){var T;(T=get(oe))==null||T.click()}async function G(T){const D=T.target,se=D.files;if(!(!se||se.length===0)){for(const te of se)try{const Oe=await ee(te);R.includes(te.type)?(set(U,[...get(U),Oe],!0),q(`Image added: ${te.name}`,"success")):(set(j,[...get(j),{name:te.name,type:te.type||"application/octet-stream",data:Oe,size:te.size}],!0),q(`File attached: ${te.name}`,"success"))}catch{q(`Failed to read: ${te.name}`,"error")}D.value=""}}function ee(T){return new Promise((D,se)=>{const te=new FileReader;te.onload=()=>D(te.result),te.onerror=()=>se(te.error),te.readAsDataURL(T)})}function ue(T){set(j,get(j).filter((D,se)=>se!==T),!0)}function _e(T){return T<1024?`${T}B`:T<1024*1024?`${(T/1024).toFixed(1)}KB`:`${(T/(1024*1024)).toFixed(1)}MB`}let Y=state(!1),Te=state(!1),Ie=state(!1),z=state(null),w=state(""),I=state(void 0),L=!1;user_effect(()=>{s()&&!L&&(requestAnimationFrame(()=>{requestAnimationFrame(()=>{var T;(T=get(I))==null||T.focus()})}),get(b)==="new"&&get(U).length===0&&setTimeout(()=>{captureViewportQuick().then(T=>{set(U,[...get(U),T],!0)}).catch(()=>{})},300)),L=s()});let X=state(""),F=state("success"),H=state(!1);function q(T,D){set(X,T,!0),set(F,D,!0),set(H,!0),setTimeout(()=>{set(H,!1)},3e3)}async function ke(){set(Te,!0);try{const T=await captureViewport();set(w,T,!0),set(z,get(U).length,!0)}catch(T){console.error("[jat-feedback] Screenshot failed:",T),q("Screenshot failed: "+(T instanceof Error?T.message:"unknown error"),"error")}finally{set(Te,!1)}}function ae(T){set(U,get(U).filter((D,se)=>se!==T),!0)}function pe(T){set(w,get(U)[T],!0),set(z,T,!0)}function Ee(T){get(z)!==null&&(get(z)>=get(U).length?(set(U,[...get(U),T],!0),q(`Screenshot captured (${get(U).length})`,"success")):(set(U,get(U).map((D,se)=>se===get(z)?T:D),!0),q("Screenshot updated","success"))),set(z,null),set(w,"")}function B(){get(z)!==null&&get(z)>=get(U).length&&(set(U,[...get(U),get(w)],!0),q(`Screenshot captured (${get(U).length})`,"success")),set(z,null),set(w,"")}function Pe(){set(Ie,!0),startElementPicker(T=>{set(W,[...get(W),T],!0),set(Ie,!1),q(`Element captured: <${T.tagName.toLowerCase()}>`,"success")})}function nt(){set(K,getCapturedLogs(),!0)}async function Ne(T){if(T.preventDefault(),!get(A).trim())return;set(Y,!0),nt();const D={};(i()||a()||l()||c())&&(D.reporter={},i()&&(D.reporter.userId=i()),a()&&(D.reporter.email=a()),l()&&(D.reporter.name=l()),c()&&(D.reporter.role=c())),(u()||d())&&(D.organization={},u()&&(D.organization.id=u()),d()&&(D.organization.name=d()));const se={title:get(A).trim(),description:get(O).trim(),type:get(C),priority:get(V),project:o()||"",page_url:window.location.href,user_agent:navigator.userAgent,console_logs:get(K).length>0?get(K):null,selected_elements:get(W).length>0?get(W):null,screenshots:get(U).length>0?get(U):null,attachments:get(j).length>0?get(j):null,metadata:Object.keys(D).length>0?D:null};try{const te=await submitReport(r(),se);te.ok?(q(`Report submitted (${te.id})`,"success"),je(),setTimeout(()=>{S(),set(b,"requests")},1200)):(enqueue(r(),se),q("Queued for retry (endpoint unreachable)","error"))}catch{enqueue(r(),se),q("Queued for retry (endpoint unreachable)","error")}finally{set(Y,!1)}}function je(){set(A,""),set(O,""),set(C,"bug"),set(V,"medium"),set(U,[],!0),set(j,[],!0),set(W,[],!0),set(K,[],!0)}user_effect(()=>{nt()});function rt(T){T.stopPropagation()}const Zt=[{value:"bug",label:"Bug"},{value:"enhancement",label:"Enhancement"},{value:"other",label:"Other"}],bn=[{value:"low",label:"Low"},{value:"medium",label:"Medium"},{value:"high",label:"High"},{value:"critical",label:"Critical"}];function be(){return get(U).length+get(j).length+get(W).length}var Ae={get endpoint(){return r()},set endpoint(T){r(T),flushSync()},get project(){return o()},set project(T){o(T),flushSync()},get isOpen(){return s()},set isOpen(T=!1){s(T),flushSync()},get userId(){return i()},set userId(T=""){i(T),flushSync()},get userEmail(){return a()},set userEmail(T=""){a(T),flushSync()},get userName(){return l()},set userName(T=""){l(T),flushSync()},get userRole(){return c()},set userRole(T=""){c(T),flushSync()},get orgId(){return u()},set orgId(T=""){u(T),flushSync()},get orgName(){return d()},set orgName(T=""){d(T),flushSync()},get onclose(){return f()},set onclose(T){f(T),flushSync()},get ongrip(){return v()},set ongrip(T){v(T),flushSync()},get agentProxy(){return _()},set agentProxy(T=""){_(T),flushSync()},get agentModel(){return y()},set agentModel(T=""){y(T),flushSync()},get agentContext(){return h()},set agentContext(T=""){h(T),flushSync()},get registeredTools(){return p()},set registeredTools(T=[]){p(T),flushSync()}},Xe=root$1(),ot=first_child(Xe),dt=child(ot),Rn=child(dt);{var nn=T=>{var D=root_1$1();delegated("mousedown",D,function(...se){var te;(te=v())==null||te.apply(this,se)}),append(T,D)};if_block(Rn,T=>{v()&&T(nn)})}var $t=sibling(Rn,2),Wt=child($t);let dn;var Tt=sibling(Wt,2);let In;var Nn=sibling(child(Tt),2);{var Bn=T=>{var D=root_2$1(),se=child(D,!0);reset(D),template_effect(()=>set_text(se,get(x))),append(T,D)};if_block(Nn,T=>{get(x)>0&&T(Bn)})}reset(Tt);var On=sibling(Tt,2);{var Ln=T=>{var D=root_3();let se;template_effect(()=>se=set_class(D,1,"tab svelte-nv4d5v",null,se,{active:get(b)==="agent"})),delegated("click",D,()=>{set(b,"agent"),set(m,!0)}),append(T,D)};if_block(On,T=>{_()&&T(Ln)})}var Pn=sibling(On,2);let yn;reset($t);var Vn=sibling($t,2);reset(dt);var wn=sibling(dt,2);{var xn=T=>{var D=root_4(),se=child(D),te=sibling(child(se),2);remove_input_defaults(te),bind_this(te,J=>set(I,J),()=>get(I)),reset(se);var Oe=sibling(se,2),Se=sibling(child(Oe),2);remove_textarea_child(Se),reset(Oe);var Je=sibling(Oe,2),jt=child(Je),$e=sibling(child(jt),2);each($e,21,()=>Zt,index,(J,de)=>{var Le=root_5(),Ue=child(Le,!0);reset(Le);var Be={};template_effect(()=>{set_text(Ue,get(de).label),Be!==(Be=get(de).value)&&(Le.value=(Le.__value=get(de).value)??"")}),append(J,Le)}),reset($e),reset(jt);var Ut=sibling(jt,2),St=sibling(child(Ut),2);each(St,21,()=>bn,index,(J,de)=>{var Le=root_6(),Ue=child(Le,!0);reset(Le);var Be={};template_effect(()=>{set_text(Ue,get(de).label),Be!==(Be=get(de).value)&&(Le.value=(Le.__value=get(de).value)??"")}),append(J,Le)}),reset(St),reset(Ut),reset(Je);var Bt=sibling(Je,2),ft=child(Bt),Ct=child(ft),pt=child(Ct);{var fn=J=>{var de=root_7();next(),append(J,de)},kn=J=>{var de=root_8(),Le=sibling(first_child(de),2);{var Ue=Be=>{var Ge=root_9(),mt=child(Ge,!0);reset(Ge),template_effect(()=>set_text(mt,get(U).length)),append(Be,Ge)};if_block(Le,Be=>{get(U).length>0&&Be(Ue)})}append(J,de)};if_block(pt,J=>{get(Te)?J(fn):J(kn,!1)})}reset(Ct);var zt=sibling(Ct,2),En=sibling(child(zt),2);{var Fe=J=>{var de=text("Click an element...");append(J,de)},qe=J=>{var de=root_11(),Le=sibling(first_child(de));{var Ue=Be=>{var Ge=root_12(),mt=child(Ge,!0);reset(Ge),template_effect(()=>set_text(mt,get(W).length)),append(Be,Ge)};if_block(Le,Be=>{get(W).length>0&&Be(Ue)})}append(J,de)};if_block(En,J=>{get(Ie)?J(Fe):J(qe,!1)})}reset(zt);var Qe=sibling(zt,2),ht=sibling(child(Qe),2);{var Vt=J=>{var de=root_13(),Le=child(de,!0);reset(de),template_effect(()=>set_text(Le,get(j).length)),append(J,de)};if_block(ht,J=>{get(j).length>0&&J(Vt)})}reset(Qe);var ct=sibling(Qe,2);bind_this(ct,J=>set(oe,J),()=>get(oe)),reset(ft);var At=sibling(ft,2);ScreenshotPreview(At,{get screenshots(){return get(U)},get capturing(){return get(Te)},oncapture:ke,onremove:ae,onedit:pe}),reset(Bt);var Gt=sibling(Bt,2);{var He=J=>{var de=root_14();each(de,21,()=>get(j),index,(Le,Ue,Be)=>{var Ge=root_15(),mt=child(Ge),Wn=child(mt);{var Tn=bt=>{var Cn=root_16();append(bt,Cn)},Gn=user_derived(()=>get(Ue).type.includes("pdf")),Yn=bt=>{var Cn=root_17();append(bt,Cn)},rn=user_derived(()=>get(Ue).type.includes("markdown")||get(Ue).name.endsWith(".md")),Sn=bt=>{var Cn=root_18();append(bt,Cn)};if_block(Wn,bt=>{get(Gn)?bt(Tn):get(rn)?bt(Yn,1):bt(Sn,!1)})}reset(mt);var Xn=sibling(mt,2),dr=child(Xn,!0);reset(Xn);var Kn=sibling(Xn,2),fr=child(Kn,!0);reset(Kn);var pr=sibling(Kn,2);reset(Ge),template_effect(bt=>{set_text(dr,get(Ue).name),set_text(fr,bt)},[()=>_e(get(Ue).size)]),delegated("click",pr,()=>ue(Be)),append(Le,Ge)}),reset(de),append(J,de)};if_block(Gt,J=>{get(j).length>0&&J(He)})}var We=sibling(Gt,2);{var Ht=J=>{var de=root_19();each(de,21,()=>get(W),index,(Le,Ue,Be)=>{var Ge=root_20(),mt=child(Ge),Wn=child(mt);reset(mt);var Tn=sibling(mt,2),Gn=child(Tn,!0);reset(Tn);var Yn=sibling(Tn,2);reset(Ge),template_effect((rn,Sn)=>{set_text(Wn,`<${rn??""}>`),set_text(Gn,Sn)},[()=>get(Ue).tagName.toLowerCase(),()=>{var rn;return((rn=get(Ue).textContent)==null?void 0:rn.substring(0,40))||get(Ue).selector}]),delegated("click",Yn,()=>{set(W,get(W).filter((rn,Sn)=>Sn!==Be),!0)}),append(Le,Ge)}),reset(de),append(J,de)};if_block(We,J=>{get(W).length>0&&J(Ht)})}var st=sibling(We,2);ConsoleLogList(st,{get logs(){return get(K)}});var it=sibling(st,2);{var gt=J=>{var de=root_21(),Le=child(de);reset(de),template_effect((Ue,Be)=>set_text(Le,`${Ue??""} attachment${Be??""} will be included`),[be,()=>be()>1?"s":""]),append(J,de)},Rt=user_derived(()=>be()>0);if_block(it,J=>{get(Rt)&&J(gt)})}var It=sibling(it,2),Yt=child(It),$n=child(Yt);reset(Yt);var vt=sibling(Yt,2),_t=sibling(vt,2),Nt=child(_t);{var pn=J=>{var de=root_22();next(),append(J,de)},ur=J=>{var de=text("Submit");append(J,de)};if_block(Nt,J=>{get(Y)?J(pn):J(ur,!1)})}reset(_t),reset(It),reset(D),template_effect(J=>{te.disabled=get(Y),Se.disabled=get(Y),$e.disabled=get(Y),St.disabled=get(Y),Ct.disabled=get(Te),zt.disabled=get(Ie),Qe.disabled=get(Y),set_text($n,`v${n}`),vt.disabled=get(Y),_t.disabled=J},[()=>get(Y)||!get(A).trim()]),event("submit",D,Ne),bind_value(te,()=>get(A),J=>set(A,J)),bind_value(Se,()=>get(O),J=>set(O,J)),bind_select_value($e,()=>get(C),J=>set(C,J)),bind_select_value(St,()=>get(V),J=>set(V,J)),delegated("click",Ct,ke),delegated("click",zt,Pe),delegated("click",Qe,M),delegated("change",ct,G),delegated("click",vt,function(...J){var de;(de=f())==null||de.apply(this,J)}),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(wn,T=>{get(b)==="new"&&T(xn)})}var Mn=sibling(wn,2);{var Hn=T=>{var D=root_24(),se=child(D);RequestList(se,{get endpoint(){return r()},get loading(){return get(me)},get error(){return get(g)},onreload:S,get reports(){return get(ve)},set reports(te){set(ve,te,!0)}}),reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Mn,T=>{get(b)==="requests"&&T(Hn)})}var Fn=sibling(Mn,2);{var qn=T=>{var D=root_25(),se=child(D);{let te=user_derived(()=>{var Oe;return((Oe=get(Q))==null?void 0:Oe.getMaxSteps())??20});AgentPanel(se,{get messages(){return get(k)},get agentState(){return get(N)},get currentStep(){return get(P)},get maxSteps(){return get(te)},get autoApprove(){return get(Z)},onsend:ie,onstop:we,onapprove:ge,onskip:ze,onautoapprovechange:xe})}reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Fn,T=>{get(b)==="agent"&&get(m)&&T(qn)})}var Dn=sibling(Fn,2);{var he=T=>{var D=root_26(),se=child(D);NotesPanel(se,{get endpoint(){return r()},get project(){return o()},onnoteschanged:ce}),reset(D),transition(3,D,()=>slide,()=>({duration:200})),append(T,D)};if_block(Dn,T=>{get(b)==="notes"&&get(E)&&T(he)})}var ye=sibling(Dn,2);StatusToast(ye,{get message(){return get(X)},get type(){return get(F)},get visible(){return get(H)}}),reset(ot);var Me=sibling(ot,2);{var Ke=T=>{AnnotationEditor(T,{get imageDataUrl(){return get(w)},onsave:Ee,oncancel:B})};if_block(Me,T=>{get(z)!==null&&T(Ke)})}return template_effect(()=>{dn=set_class(Wt,1,"tab svelte-nv4d5v",null,dn,{active:get(b)==="new"}),In=set_class(Tt,1,"tab svelte-nv4d5v",null,In,{active:get(b)==="requests"}),yn=set_class(Pn,1,"tab svelte-nv4d5v",null,yn,{active:get(b)==="notes"})}),delegated("keydown",ot,rt),delegated("keyup",ot,rt),event("keypress",ot,rt),delegated("click",Wt,()=>set(b,"new")),delegated("click",Tt,()=>set(b,"requests")),delegated("click",Pn,()=>{set(b,"notes"),set(E,!0)}),delegated("click",Vn,function(...T){var D;(D=f())==null||D.apply(this,T)}),append(e,Xe),pop(Ae)}delegate(["keydown","keyup","mousedown","click","change"]),create_custom_element(FeedbackPanel,{endpoint:{},project:{},isOpen:{},userId:{},userEmail:{},userName:{},userRole:{},orgId:{},orgName:{},onclose:{},ongrip:{},agentProxy:{},agentModel:{},agentContext:{},registeredTools:{}},[],[],{mode:"open"});var root_1=from_html("<div><!></div>"),root_2=from_html('<div class="jat-feedback-panel svelte-qpyrvv"><div class="no-endpoint svelte-qpyrvv"><p class="svelte-qpyrvv">No endpoint configured.</p> <p class="svelte-qpyrvv">Add the <code class="svelte-qpyrvv">endpoint</code> attribute:</p> <code class="example svelte-qpyrvv">&lt;jat-feedback endpoint="http://localhost:3333"&gt;</code></div></div>'),root=from_html('<div class="jat-feedback-root svelte-qpyrvv" data-page-agent-not-interactive=""><!> <!></div>');const $$css={hash:"svelte-qpyrvv",code:`.jat-feedback-root.svelte-qpyrvv {position:fixed;z-index:2147483647;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;}.jat-feedback-panel.svelte-qpyrvv {position:absolute;
379
379
  animation: svelte-qpyrvv-panel-in 0.2s ease;}.jat-feedback-panel.hidden.svelte-qpyrvv {display:none;}.jat-feedback-panel.dragging.svelte-qpyrvv {pointer-events:none;opacity:0.9;}.no-endpoint.svelte-qpyrvv {width:320px;background:#111827;border:1px solid #374151;border-radius:12px;padding:20px;color:#d1d5db;font-size:13px;box-shadow:0 20px 60px rgba(0,0,0,0.4);}.no-endpoint.svelte-qpyrvv p:where(.svelte-qpyrvv) {margin:0 0 8px;}.no-endpoint.svelte-qpyrvv code:where(.svelte-qpyrvv) {font-family:'SF Mono', 'Fira Code', monospace;font-size:11px;color:#93c5fd;}.no-endpoint.svelte-qpyrvv code.example:where(.svelte-qpyrvv) {display:block;background:#1f2937;padding:8px 10px;border-radius:4px;margin-top:4px;}
380
380
  @keyframes svelte-qpyrvv-panel-in {
381
381
  from { opacity: 0; transform: translateY(8px) scale(0.96); }
@@ -3719,6 +3719,8 @@ async function fetchReports(e) {
3719
3719
  credentials: "same-origin"
3720
3720
  });
3721
3721
  if (!n.ok) {
3722
+ if (n.status === 401 || n.status === 403)
3723
+ return { reports: [] };
3722
3724
  const o = await n.json().catch(() => ({ error: `HTTP ${n.status}` }));
3723
3725
  return { reports: [], error: o.error || `HTTP ${n.status}` };
3724
3726
  }
@@ -12287,7 +12289,7 @@ const $$css$1 = {
12287
12289
  };
12288
12290
  function FeedbackPanel(e, t) {
12289
12291
  push(t, !0), append_styles(e, $$css$1);
12290
- const n = "3.0.0";
12292
+ const n = "3.0.1";
12291
12293
  let r = prop(t, "endpoint", 7), o = prop(t, "project", 7), s = prop(t, "isOpen", 7, !1), i = prop(t, "userId", 7, ""), a = prop(t, "userEmail", 7, ""), l = prop(t, "userName", 7, ""), c = prop(t, "userRole", 7, ""), u = prop(t, "orgId", 7, ""), d = prop(t, "orgName", 7, ""), f = prop(t, "onclose", 7), v = prop(t, "ongrip", 7), _ = prop(t, "agentProxy", 7, ""), y = prop(t, "agentModel", 7, ""), h = prop(t, "agentContext", 7, ""), p = prop(t, "registeredTools", 23, () => []), b = /* @__PURE__ */ state("new"), m = /* @__PURE__ */ state(!1), E = /* @__PURE__ */ state(!1), k = /* @__PURE__ */ state(proxy([])), N = /* @__PURE__ */ state("idle"), P = /* @__PURE__ */ state(0), Z = /* @__PURE__ */ state(!1), Q = /* @__PURE__ */ state(null);
12292
12294
  function re() {
12293
12295
  return get(Q) || set(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jat-feedback",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Embeddable feedback widget for bug reports and feature requests. Captures screenshots, console logs, and user context as a web component.",
5
5
  "type": "module",
6
6
  "main": "dist/jat-feedback.js",
@@ -31,14 +31,24 @@ export const load: PageServerLoad = async ({
31
31
  query = query.eq("priority", priority)
32
32
  }
33
33
 
34
- const { data: tasks, error } = await query.limit(100)
34
+ // Fetch tasks and team members in parallel
35
+ // NOTE: Customize the role filter for your project's profile roles
36
+ const [tasksResult, teamResult] = await Promise.all([
37
+ query.limit(100),
38
+ supabase
39
+ .from("profiles")
40
+ .select("id, full_name, role")
41
+ .not("full_name", "is", null)
42
+ .order("full_name"),
43
+ ])
35
44
 
36
- if (error) {
37
- console.error("Failed to load tasks:", error.message)
45
+ if (tasksResult.error) {
46
+ console.error("Failed to load tasks:", tasksResult.error.message)
38
47
  }
39
48
 
40
49
  return {
41
- tasks: tasks ?? [],
50
+ tasks: tasksResult.data ?? [],
51
+ teamMembers: teamResult.data ?? [],
42
52
  filters: { status, issueType, priority },
43
53
  user,
44
54
  }
@@ -1,9 +1,53 @@
1
1
  <script lang="ts">
2
- import { goto } from "$app/navigation"
2
+ import { goto, invalidate } from "$app/navigation"
3
3
  import { page } from "$app/state"
4
+ import { onMount } from "svelte"
5
+ import { SearchDropdown, InlineEdit } from "@joewinke/jatui"
6
+ import type { SearchDropdownGroup } from "@joewinke/jatui"
4
7
 
5
8
  let { data } = $props()
6
9
 
10
+ // ── Reactive task list (server data + realtime inserts) ──
11
+ let tasks = $state(data.tasks)
12
+
13
+ // Sync when server data changes (e.g. filter navigation)
14
+ $effect(() => {
15
+ tasks = data.tasks
16
+ })
17
+
18
+ // Realtime subscription — new/updated rows refresh the list
19
+ onMount(() => {
20
+ const channel = data.supabase
21
+ .channel("project_tasks_changes")
22
+ .on(
23
+ "postgres_changes",
24
+ { event: "*", schema: "public", table: "project_tasks" },
25
+ async () => {
26
+ // Re-fetch with current filters applied
27
+ let query = data.supabase
28
+ .from("project_tasks")
29
+ .select("*, project_tasks_comments(count)")
30
+ .order("created_at", { ascending: false })
31
+ .limit(100)
32
+
33
+ const status = page.url.searchParams.get("status")
34
+ const issueType = page.url.searchParams.get("type")
35
+ const priority = page.url.searchParams.get("priority")
36
+ if (status) query = query.eq("status", status)
37
+ if (issueType) query = query.eq("issue_type", issueType)
38
+ if (priority) query = query.eq("priority", priority)
39
+
40
+ const { data: fresh } = await query
41
+ if (fresh) tasks = fresh
42
+ },
43
+ )
44
+ .subscribe()
45
+
46
+ return () => {
47
+ data.supabase.removeChannel(channel)
48
+ }
49
+ })
50
+
7
51
  // ── Task detail drawer state ──
8
52
  let selectedTask: (typeof data.tasks)[0] | null = $state(null)
9
53
  let comments: { id: string; author: string; author_role: string; text: string; created_at: string }[] = $state([])
@@ -187,6 +231,77 @@
187
231
  }
188
232
  return 0
189
233
  }
234
+
235
+ // ── Assignee dropdown ──
236
+ const assigneeGroups: SearchDropdownGroup[] = $derived.by(() => {
237
+ const members = data.teamMembers || []
238
+ const options = [
239
+ { value: "", label: "Unassigned" },
240
+ ...members
241
+ .filter((m: { full_name: string | null }) => m.full_name)
242
+ .map((m: { full_name: string | null }) => ({
243
+ value: m.full_name!,
244
+ label: m.full_name!,
245
+ })),
246
+ ]
247
+ return [{ label: "Team", options }]
248
+ })
249
+
250
+ // ── Assignee update (wraps generic update, clears to null for empty) ──
251
+ function updateAssignee(taskId: string, value: string) {
252
+ updateTask(taskId, "assignee", value || null)
253
+ }
254
+
255
+ // ── Generic task update ──
256
+ async function updateTask(taskId: string, field: string, value: string | null) {
257
+ const { error } = await data.supabase
258
+ .from("project_tasks")
259
+ .update({ [field]: value })
260
+ .eq("id", taskId)
261
+
262
+ if (error) {
263
+ console.error(`Failed to update ${field}:`, error.message)
264
+ return
265
+ }
266
+
267
+ tasks = tasks.map((t) =>
268
+ t.id === taskId ? { ...t, [field]: value } : t,
269
+ )
270
+ if (selectedTask?.id === taskId) {
271
+ selectedTask = { ...selectedTask, [field]: value }
272
+ }
273
+ }
274
+
275
+ // ── Dropdown groups for editable fields ──
276
+ const typeGroups: SearchDropdownGroup[] = [
277
+ { label: "Type", options: [
278
+ { value: "bug", label: "Bug", icon: "🐛" },
279
+ { value: "feature", label: "Feature", icon: "✨" },
280
+ { value: "task", label: "Task", icon: "📋" },
281
+ { value: "epic", label: "Epic", icon: "🏔️" },
282
+ ]},
283
+ ]
284
+
285
+ const statusGroups: SearchDropdownGroup[] = [
286
+ { label: "Status", options: [
287
+ { value: "submitted", label: "Submitted" },
288
+ { value: "in_progress", label: "In Progress" },
289
+ { value: "completed", label: "Completed" },
290
+ { value: "accepted", label: "Accepted" },
291
+ { value: "rejected", label: "Rejected" },
292
+ { value: "wontfix", label: "Won't Fix" },
293
+ { value: "closed", label: "Closed" },
294
+ ]},
295
+ ]
296
+
297
+ const priorityGroups: SearchDropdownGroup[] = [
298
+ { label: "Priority", options: [
299
+ { value: "critical", label: "Critical" },
300
+ { value: "high", label: "High" },
301
+ { value: "medium", label: "Medium" },
302
+ { value: "low", label: "Low" },
303
+ ]},
304
+ ]
190
305
  </script>
191
306
 
192
307
  <svelte:head>
@@ -204,7 +319,7 @@
204
319
  </p>
205
320
  </div>
206
321
  <div class="text-sm text-base-content/50">
207
- {data.tasks.length} task{data.tasks.length !== 1 ? "s" : ""}
322
+ {tasks.length} task{tasks.length !== 1 ? "s" : ""}
208
323
  </div>
209
324
  </div>
210
325
 
@@ -251,7 +366,7 @@
251
366
  </div>
252
367
 
253
368
  <!-- Task list -->
254
- {#if data.tasks.length === 0}
369
+ {#if tasks.length === 0}
255
370
  <div class="text-center py-16 text-base-content/50">
256
371
  <div class="text-4xl mb-3">📭</div>
257
372
  <p class="text-lg font-medium">No tasks found</p>
@@ -265,8 +380,8 @@
265
380
  </div>
266
381
  {:else}
267
382
  <!-- Desktop table -->
268
- <div class="hidden sm:block overflow-x-auto">
269
- <table class="table table-sm">
383
+ <div class="hidden sm:block overflow-x-auto w-full">
384
+ <table class="table table-sm w-full">
270
385
  <thead>
271
386
  <tr class="text-base-content/60">
272
387
  <th>Title</th>
@@ -279,29 +394,50 @@
279
394
  </tr>
280
395
  </thead>
281
396
  <tbody>
282
- {#each data.tasks as task}
397
+ {#each tasks as task (task.id)}
283
398
  <tr
284
399
  class="hover:bg-base-200/50 cursor-pointer transition-colors"
285
400
  onclick={() => openTask(task)}
286
401
  >
287
- <td class="font-medium max-w-xs truncate">{task.title}</td>
288
- <td>
289
- <span class="text-sm" title={task.issue_type}>
290
- {typeIcon(task.issue_type)} {task.issue_type}
291
- </span>
402
+ <td class="font-medium max-w-xs" onclick={(e) => e.stopPropagation()}>
403
+ <InlineEdit
404
+ value={task.title}
405
+ onSave={(v) => updateTask(task.id, "title", v)}
406
+ truncate
407
+ placeholder="Untitled"
408
+ />
292
409
  </td>
293
- <td>
294
- <span class="badge badge-sm {statusColor(task.status)}">
295
- {statusLabel(task.status)}
296
- </span>
410
+ <td onclick={(e) => e.stopPropagation()}>
411
+ <SearchDropdown
412
+ value={task.issue_type}
413
+ groups={typeGroups}
414
+ placeholder="Type..."
415
+ onChange={(v) => updateTask(task.id, "issue_type", v)}
416
+ />
297
417
  </td>
298
- <td>
299
- <span class={priorityColor(task.priority)}>
300
- {task.priority || "—"}
301
- </span>
418
+ <td onclick={(e) => e.stopPropagation()}>
419
+ <SearchDropdown
420
+ value={task.status}
421
+ groups={statusGroups}
422
+ placeholder="Status..."
423
+ onChange={(v) => updateTask(task.id, "status", v)}
424
+ />
302
425
  </td>
303
- <td class="text-sm text-base-content/60">
304
- {task.assignee || "—"}
426
+ <td onclick={(e) => e.stopPropagation()}>
427
+ <SearchDropdown
428
+ value={task.priority || ""}
429
+ groups={priorityGroups}
430
+ placeholder="Priority..."
431
+ onChange={(v) => updateTask(task.id, "priority", v || null)}
432
+ />
433
+ </td>
434
+ <td class="text-sm" onclick={(e) => e.stopPropagation()}>
435
+ <SearchDropdown
436
+ value={task.assignee || ""}
437
+ groups={assigneeGroups}
438
+ placeholder="Assign..."
439
+ onChange={(v) => updateAssignee(task.id, v)}
440
+ />
305
441
  </td>
306
442
  <td class="text-sm text-base-content/60">
307
443
  {formatDate(task.created_at)}
@@ -321,7 +457,7 @@
321
457
 
322
458
  <!-- Mobile cards -->
323
459
  <div class="sm:hidden flex flex-col gap-3">
324
- {#each data.tasks as task}
460
+ {#each tasks as task (task.id)}
325
461
  <button
326
462
  class="card bg-base-100 shadow-sm border border-base-300 p-4 text-left w-full"
327
463
  onclick={() => openTask(task)}
@@ -337,9 +473,14 @@
337
473
  </div>
338
474
  <div class="flex items-center gap-3 mt-2 text-xs text-base-content/50">
339
475
  <span class={priorityColor(task.priority)}>{task.priority || "—"}</span>
340
- {#if task.assignee}
341
- <span>{task.assignee}</span>
342
- {/if}
476
+ <span onclick={(e) => e.stopPropagation()}>
477
+ <SearchDropdown
478
+ value={task.assignee || ""}
479
+ groups={assigneeGroups}
480
+ placeholder="Assign..."
481
+ onChange={(v) => updateAssignee(task.id, v)}
482
+ />
483
+ </span>
343
484
  <span>{formatDate(task.created_at)}</span>
344
485
  {#if commentCount(task) > 0}
345
486
  <span>💬 {commentCount(task)}</span>
@@ -364,9 +505,14 @@
364
505
  <div class="fixed inset-y-0 right-0 z-50 w-full max-w-lg bg-base-100 shadow-xl flex flex-col overflow-hidden">
365
506
  <!-- Header -->
366
507
  <div class="flex items-center justify-between px-5 py-4 border-b border-base-300">
367
- <div class="flex items-center gap-2 min-w-0">
508
+ <div class="flex items-center gap-2 min-w-0 flex-1">
368
509
  <span>{typeIcon(selectedTask.issue_type)}</span>
369
- <h2 class="text-lg font-bold truncate">{selectedTask.title}</h2>
510
+ <InlineEdit
511
+ value={selectedTask.title}
512
+ onSave={(v) => updateTask(selectedTask!.id, "title", v)}
513
+ class="text-lg font-bold"
514
+ placeholder="Untitled"
515
+ />
370
516
  </div>
371
517
  <button class="btn btn-ghost btn-sm btn-square" onclick={closeDrawer}>
372
518
 
@@ -375,17 +521,26 @@
375
521
 
376
522
  <!-- Scrollable content -->
377
523
  <div class="flex-1 overflow-y-auto px-5 py-4">
378
- <!-- Meta badges -->
524
+ <!-- Editable fields -->
379
525
  <div class="flex flex-wrap gap-2 mb-4">
380
- <span class="badge {statusColor(selectedTask.status)}">
381
- {statusLabel(selectedTask.status)}
382
- </span>
383
- <span class="badge badge-outline">{selectedTask.issue_type}</span>
384
- {#if selectedTask.priority}
385
- <span class="badge badge-outline {priorityColor(selectedTask.priority)}">
386
- {selectedTask.priority}
387
- </span>
388
- {/if}
526
+ <SearchDropdown
527
+ value={selectedTask.status}
528
+ groups={statusGroups}
529
+ placeholder="Status..."
530
+ onChange={(v) => updateTask(selectedTask!.id, "status", v)}
531
+ />
532
+ <SearchDropdown
533
+ value={selectedTask.issue_type}
534
+ groups={typeGroups}
535
+ placeholder="Type..."
536
+ onChange={(v) => updateTask(selectedTask!.id, "issue_type", v)}
537
+ />
538
+ <SearchDropdown
539
+ value={selectedTask.priority || ""}
540
+ groups={priorityGroups}
541
+ placeholder="Priority..."
542
+ onChange={(v) => updateTask(selectedTask!.id, "priority", v || null)}
543
+ />
389
544
  {#if selectedTask.source && selectedTask.source !== "feedback"}
390
545
  <span class="badge badge-ghost">{selectedTask.source}</span>
391
546
  {/if}
@@ -393,10 +548,15 @@
393
548
 
394
549
  <!-- Details grid -->
395
550
  <div class="grid grid-cols-2 gap-x-4 gap-y-2 text-sm mb-6">
396
- {#if selectedTask.assignee}
397
- <div class="text-base-content/50">Assignee</div>
398
- <div>{selectedTask.assignee}</div>
399
- {/if}
551
+ <div class="text-base-content/50">Assignee</div>
552
+ <div>
553
+ <SearchDropdown
554
+ value={selectedTask.assignee || ""}
555
+ groups={assigneeGroups}
556
+ placeholder="Assign..."
557
+ onChange={(v) => updateAssignee(selectedTask!.id, v)}
558
+ />
559
+ </div>
400
560
  {#if selectedTask.due_date}
401
561
  <div class="text-base-content/50">Due date</div>
402
562
  <div>{formatDate(selectedTask.due_date)}</div>
@@ -443,8 +603,9 @@
443
603
  <h3 class="text-sm font-semibold text-base-content/60 mb-2">Screenshots</h3>
444
604
  <div class="flex gap-2 flex-wrap">
445
605
  {#each selectedTask.screenshot_paths as path}
446
- <a href={path} target="_blank" rel="noopener" class="block">
447
- <img src={path} alt="Screenshot" class="w-32 h-24 object-cover rounded border border-base-300" />
606
+ {@const proxyUrl = `/api/feedback/screenshots/${path}`}
607
+ <a href={proxyUrl} target="_blank" rel="noopener" class="block">
608
+ <img src={proxyUrl} alt="Screenshot" class="w-32 h-24 object-cover rounded border border-base-300" />
448
609
  </a>
449
610
  {/each}
450
611
  </div>
@@ -511,3 +672,27 @@
511
672
  </div>
512
673
  </div>
513
674
  {/if}
675
+
676
+ <style>
677
+ /* Elevate table cells and cards when a SearchDropdown is open,
678
+ so the panel renders above subsequent sibling rows/cards */
679
+ :global(td:has(.sd-panel)) {
680
+ position: relative;
681
+ z-index: 100;
682
+ }
683
+ :global(button.card:has(.sd-panel)) {
684
+ z-index: 100;
685
+ }
686
+ /* Ensure panel has an opaque, elevated background */
687
+ :global(.sd-panel) {
688
+ background: var(--color-base-300, var(--color-base-100, Canvas)) !important;
689
+ box-shadow: 0 8px 32px oklch(0 0 0 / 0.5) !important;
690
+ }
691
+ /* Hover highlight on dropdown options */
692
+ :global(.sd-panel .sd-option:hover) {
693
+ background: color-mix(in oklch, var(--color-base-content, CanvasText) 10%, var(--color-base-300, Canvas)) !important;
694
+ }
695
+ :global(.sd-panel .sd-option-selected) {
696
+ background: color-mix(in oklch, var(--color-primary, LinkText) 15%, var(--color-base-300, Canvas)) !important;
697
+ }
698
+ </style>