@mindstudio-ai/browser-agent 0.1.53 → 0.1.55

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/index.js +1 -1
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -108,7 +108,7 @@ ${g}
108
108
  white-space: nowrap;
109
109
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
110
110
  ">Remy</div>
111
- `,t.style.cssText=["position: fixed","z-index: 2147483647","pointer-events: none","opacity: 0","transition: opacity 300ms ease, left 600ms cubic-bezier(0.4, 0, 0, 1), top 600ms cubic-bezier(0.4, 0, 0, 1)","will-change: left, top, opacity"].join("; ");let r=document.createElement("div");r.id=pn,r.style.cssText=["position: fixed","top: 0","left: 0","width: 100vw","height: 100vh","background: white","opacity: 0","z-index: 2147483646","pointer-events: none","transition: opacity 600ms ease-out"].join("; "),document.documentElement.appendChild(r),document.documentElement.appendChild(t),e.cameraEl=t,e.flashEl=r}function Bu(e,t){let{cameraEl:r}=A().screenshot;r&&(r.style.left=`${e}px`,r.style.top=`${t}px`,r.style.opacity="1")}function $i(e,t){let{cameraEl:r}=A().screenshot;r&&(r.style.left=`${e}px`,r.style.top=`${t}px`)}async function Uu(){let{flashEl:e}=A().screenshot;e&&(e.style.transition="none",e.style.opacity="0.7",e.offsetWidth,e.style.transition="opacity 600ms ease-out",e.style.opacity="0",await oe(600))}function Hu(){let{cameraEl:e}=A().screenshot;e&&(e.style.opacity="0")}function Vu(){let e=A().screenshot;document.getElementById(mn)?.remove(),document.getElementById(pn)?.remove(),e.cameraEl=null,e.flashEl=null}function Gu(e,t,r){return new Promise(n=>{let o=e.scrollTop,i=t-o;if(Math.abs(i)<1){n();return}let a=Math.abs(i),c=Math.max(r,a/2),l=performance.now();function u(d){let f=d-l,m=Math.min(f/c,1),p=m<.5?4*m*m*m:1-Math.pow(-2*m+2,3)/2;e.scrollTop=o+i*p,m<1?requestAnimationFrame(u):n()}requestAnimationFrame(u)})}async function At(e){let t=A().screenshot;if(t.capturing)throw new Error("Screenshot already in progress");t.capturing=!0,_i();try{return await zu(e)}finally{t.capturing=!1,Oi()}}async function zu(e){await fi();let t=window.innerWidth,r=window.innerHeight;if(e?.viewportOnly)return await qu(t,r);let n=We(),o=n!==document.documentElement,i=n.scrollTop,a=n.style.scrollBehavior;n.style.scrollBehavior="auto",Wu();try{let s=Math.random()<.5?t+30:-60,c=r*.3+Math.random()*r*.4;Bu(s,c),await oe(200);let l=t*.4+Math.random()*t*.15,u=r*.35+Math.random()*r*.15;$i(l,u),await oe(700);let d=[],f=0,m=o?n.clientHeight:r,p=()=>o?n.scrollHeight:Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),g=50,h=p();for(;f<h&&d.length<g;){let x=d.length,N=p();if(N<=0){console.warn("[screenshot] skipping chunk \u2014 page height is 0");break}if(N-f<m*.05&&d.length>0)break;let W=N-m,L=Math.min(f,W);Math.abs(L-n.scrollTop)>1&&(await Gu(n,L,Du),$i(l,u),await oe(Ou)),Uu();let O=n.scrollTop,V=Math.min(m,N-O);if(V<=0){console.warn("[screenshot] skipping chunk \u2014 captureHeight is 0 (page may have shrunk)"),f+=m;continue}let K=o?n.getBoundingClientRect():null;try{let F=await(await j(document.body,{embedFonts:!0,exclude:Wi,plugins:[Pi]})).toCanvas({width:t});if(F&&K){let D=F.width/t,_=F.width;if(x===0&&K.top>0){let M=Math.round(K.top*D),G=document.createElement("canvas");G.width=_,G.height=M;let J=G.getContext("2d");J&&(J.drawImage(F,0,0,_,M,0,0,_,M),d.push({y:0,canvas:G}))}let ee=Math.round(K.top*D),$=Math.round(V*D),z=document.createElement("canvas");z.width=_,z.height=$;let X=z.getContext("2d");X&&(X.drawImage(F,0,ee,_,$,0,0,_,$),d.push({y:K.top+O,canvas:z}));let ne=K.bottom;if(f+m>=h&&ne<r){let M=Math.round(ne*D),G=F.height-M;if(G>0){let J=document.createElement("canvas");J.width=_,J.height=G;let Y=J.getContext("2d");Y&&(Y.drawImage(F,0,M,_,G,0,0,_,G),d.push({y:K.top+N,canvas:J}))}}}else if(F){let D=F.width/t,_=Math.round(O*D),ee=Math.min(O+V,N),$=Math.round(ee*D)-_,z=F.width,X=document.createElement("canvas");X.width=z,X.height=$;let ne=X.getContext("2d");ne&&(ne.drawImage(F,0,_,z,$,0,0,z,$),d.push({y:O,canvas:X}))}}catch(R){console.error("[screenshot] snapdom capture failed for chunk",x,R)}f+=m}if(f<h&&console.warn(`[screenshot] page truncated \u2014 captured ${d.length} chunks but page is ${h}px tall`),d.length===0)throw new Error("Screenshot capture produced no image data");let y=d[0].canvas.width,v=0;for(let x of d)v+=x.canvas.height;let w=document.createElement("canvas");w.width=y,w.height=v;let b=w.getContext("2d");if(!b)throw new Error("Failed to create output canvas");let S=0;for(let x of d)b.drawImage(x.canvas,0,S),S+=x.canvas.height;let C=w.toDataURL("image/jpeg",1).replace(/^data:image\/jpeg;base64,/,"");if(!C)throw new Error("Canvas export produced no image data");return{image:C,width:w.width,height:w.height}}finally{Hu(),Vu(),n.style.scrollBehavior="auto",n.scrollTop=i,n.style.scrollBehavior=a}}function Yu(){let e=document.getElementById("__mindstudio-notes-overlay");if(!e)return null;let t=window.scrollX,r=window.scrollY,n=e.style.cssText;return e.style.position="absolute",e.style.inset="auto",e.style.left=`${t}px`,e.style.top=`${r}px`,e.style.width=`${window.innerWidth}px`,e.style.height=`${window.innerHeight}px`,()=>{e.style.cssText=n}}async function qu(e,t){let r=Yu();try{let o=await(await j(document.body,{embedFonts:!0,exclude:Wi,plugins:[Pi]})).toCanvas({width:e*Ve});if(!o)throw new Error("SnapDOM viewport capture returned empty result");let i=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),a=document.createElement("canvas");a.width=e*Ve,a.height=t*Ve;let s=a.getContext("2d");if(!s)throw new Error("Failed to create canvas context");let c=window.scrollY/i*o.height,l=t/i*o.height;s.drawImage(o,0,c,o.width,l,0,0,e*Ve,t*Ve);let d=a.toDataURL("image/jpeg",1).replace(/^data:image\/jpeg;base64,/,"");if(!d)throw new Error("Canvas export produced no image data");return{image:d,width:e*Ve,height:t*Ve}}finally{r?.()}}var ju=["backgroundColor","color","fontFamily","fontSize","fontWeight","fontStyle","lineHeight","letterSpacing","textAlign","textDecoration","textTransform","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","borderRadius","borderColor","borderWidth","borderStyle","boxShadow","opacity","width","height","display","flexDirection","justifyContent","alignItems","gap"];function Zu(e,t){let r=t??ju;if(e.ref||e.text||e.role||e.label||e.selector){let{element:i,matched:a}=Me(e);return{...Bi(i,e.ref,r),matched:a}}let n=Je(),o=[];for(let[i,a]of n)a.isConnected&&o.push(Bi(a,i,r));return o}function Bi(e,t,r){let n=window.getComputedStyle(e),o={};t&&(o.ref=t),o.tag=e.tagName.toLowerCase();let i=e.textContent?.trim();i&&(o.text=i.length>60?i.slice(0,60)+"\u2026":i);for(let a of r){let s=n.getPropertyValue(a.replace(/([A-Z])/g,"-$1").toLowerCase());s&&(o[a]=s)}return o}var Ui=150,rr=100,Et="__mindstudio_pending_nav",Hi=300,yn=1e4;function Xu(e){try{let t=new URL(e,window.location.origin);return t.origin!==window.location.origin?!1:t.pathname===window.location.pathname&&t.search===window.location.search&&t.hash===window.location.hash}catch{return!1}}function Vi(e){try{let t=new URL(e,window.location.origin);return t.origin!==window.location.origin?!1:(Xu(e)||(window.history.pushState(null,"",t.pathname+t.search+t.hash),window.dispatchEvent(new PopStateEvent("popstate"))),!0)}catch{return!1}}async function hn(e=300,t=2e3){return new Promise(r=>{let n=Date.now()+t,o=Date.now(),i=We(),a=window.scrollY,s=window.scrollX,c=document.body.scrollTop,l=document.documentElement.scrollTop,u=document.body.scrollHeight,d=document.body.scrollWidth,f=i.scrollTop,m=i.scrollLeft;function p(){if(Date.now()>n){r();return}let g=window.scrollY,h=window.scrollX,y=document.body.scrollTop,v=document.documentElement.scrollTop,w=document.body.scrollHeight,b=document.body.scrollWidth,S=i.scrollTop,I=i.scrollLeft;(g!==a||h!==s||y!==c||v!==l||w!==u||b!==d||S!==f||I!==m)&&(a=g,s=h,c=y,l=v,u=w,d=b,f=S,m=I,o=Date.now()),Date.now()-o>=e?r():requestAnimationFrame(p)}requestAnimationFrame(p)})}var Ku=350;async function gn(e){let t=Ju(e),r=t.map(a=>({el:a,top:a.scrollTop,left:a.scrollLeft})),n=document.documentElement.style.scrollBehavior;document.documentElement.style.scrollBehavior="auto",e.scrollIntoView({block:"center",inline:"nearest",behavior:"instant"}),document.documentElement.style.scrollBehavior=n;let o=t.map(a=>({top:a.scrollTop,left:a.scrollLeft})),i=[];for(let a=0;a<t.length;a++)(r[a].top!==o[a].top||r[a].left!==o[a].left)&&i.push({el:t[a],fromTop:r[a].top,fromLeft:r[a].left,toTop:o[a].top,toLeft:o[a].left});if(i.length===0){await new Promise(a=>requestAnimationFrame(()=>a()));return}for(let a of i)a.el.scrollTop=a.fromTop,a.el.scrollLeft=a.fromLeft;await new Promise(a=>{let s=performance.now();function c(l){let u=l-s,d=Math.min(u/Ku,1),f=1-Math.pow(1-d,3);for(let m of i)m.el.scrollTop=m.fromTop+(m.toTop-m.fromTop)*f,m.el.scrollLeft=m.fromLeft+(m.toLeft-m.fromLeft)*f;d<1?requestAnimationFrame(c):a()}requestAnimationFrame(c)})}function Ju(e){let t=[],r=e.parentElement;for(;r;){let n=getComputedStyle(r),o=n.overflowY,i=n.overflowX;(o==="auto"||o==="scroll"||i==="auto"||i==="scroll")&&t.push(r),r=r.parentElement}return t.push(document.scrollingElement||document.documentElement),t}async function nr(e,t){let r=Date.now(),n=[],o=[],i="",a=!1,s=new Set(["click","type","select"]),c=t.some(l=>s.has(l.command));c&&(It(!0),dn()),Fr();try{for(let l=0;l<t.length;l++){let u=t[l],d=u.command;try{if(l>0&&await oe(Ui+Math.random()*rr-rr/2),d==="navigate"){let m=u.url;if(!m)throw new Error('navigate command requires a "url" field');if(Vi(m)){n.push({index:l,command:d,result:"ok"}),await oe(Hi);continue}let p=t.slice(l+1);if(p.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e,startTime:r,completedSteps:n,remainingSteps:p,stepOffset:l+1}))}catch{}n.push({index:l,command:d,result:"ok"}),a=!0,window.location.href=m,await new Promise((g,h)=>{setTimeout(()=>h(new Error("Navigation did not complete within timeout")),yn)})}if(d==="reload"){let m=t.slice(l+1);if(m.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e,startTime:r,completedSteps:n,remainingSteps:m,stepOffset:l+1}))}catch{}n.push({index:l,command:d,result:"ok"}),a=!0,window.location.reload(),await new Promise((p,g)=>{setTimeout(()=>g(new Error("Reload did not complete within timeout")),yn)})}let f=await zi(u);n.push({index:l,command:d,...f})}catch(f){n.push({index:l,command:d,error:f instanceof Error?f.message:String(f)});break}}try{i=await Qe()}catch{}}finally{o=_r(),c&&(It(!1),er())}return{id:e,steps:n,snapshot:i,logs:o,duration:Date.now()-r}}async function Gi(){let e=null;try{let i=sessionStorage.getItem(Et);if(!i)return null;sessionStorage.removeItem(Et),e=JSON.parse(i)}catch{return null}if(!e)return null;let t=[...e.completedSteps],r=[],n="",o=e.remainingSteps.some(i=>i.command==="click"||i.command==="type"||i.command==="select");o&&(It(!0),dn()),Fr();try{for(let i=0;i<e.remainingSteps.length;i++){let a=e.remainingSteps[i],s=a.command,c=e.stepOffset+i;try{if(i>0&&await oe(Ui+Math.random()*rr-rr/2),s==="navigate"){let u=a.url;if(!u)throw new Error('navigate command requires a "url" field');if(Vi(u)){t.push({index:c,command:s,result:"ok"}),await oe(Hi);continue}let d=e.remainingSteps.slice(i+1);if(d.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e.id,startTime:e.startTime,completedSteps:t,remainingSteps:d,stepOffset:c+1}))}catch{}t.push({index:c,command:s,result:"ok"}),window.location.href=u,await new Promise((f,m)=>{setTimeout(()=>m(new Error("Navigation did not complete within timeout")),yn)})}let l=await zi(a);t.push({index:c,command:s,...l})}catch(l){t.push({index:c,command:s,error:l instanceof Error?l.message:String(l)});break}}try{n=await Qe()}catch{}}finally{r=_r(),o&&(It(!1),er())}return{id:e.id,steps:t,snapshot:n,logs:r,duration:Date.now()-e.startTime}}async function zi(e){switch(e.command){case"snapshot":return{result:await Qe()};case"click":{let{element:t,matched:r}=Me(e);return await gn(t),await Qt(t),await Fi(),Wr(t),await hn(),await oe(1e3),{result:"ok",matched:r}}case"type":{let{element:t,matched:r}=Me(e),n=e.text;if(!n&&n!=="")throw new Error('type command requires a "text" field');return await gn(t),await Qt(t),await no(t,n,{clear:!!e.clear}),await hn(),{result:"ok",matched:r}}case"select":{let{element:t,matched:r}=Me(e),n=e.option;if(!n)throw new Error('select command requires an "option" field');await gn(t),await Qt(t);let o=io(t,n);return await hn(),{result:`selected "${o}"`,matched:r}}case"wait":{let{matched:t,elapsed:r}=await oo(e);return{result:"ok",matched:t,elapsed:r}}case"styles":{let t=e.properties;return{result:Zu(e,t)}}case"evaluate":{let t=e.script;if(!t)throw new Error('evaluate command requires a "script" field');return{result:await ao(t)}}case"screenshotFullPage":{let t=We();t.scrollTop=0,await oe(500);let{image:r,width:n,height:o}=await At(),i=e.uploadUrl,a=e.uploadFields;if(i&&a){let s=atob(r),c=new Uint8Array(s.length);for(let f=0;f<s.length;f++)c[f]=s.charCodeAt(f);let l=new FormData;for(let[f,m]of Object.entries(a))l.append(f,m);l.append("file",new Blob([c],{type:"image/jpeg"}),"screenshot.jpg");let u=await fetch(i,{method:"POST",body:l});if(!u.ok)throw new Error(`Screenshot upload failed: ${u.status}`);let d=e._publicUrl;return{result:{width:n,height:o,uploaded:!0,...d?{_publicUrl:d}:{}}}}return{result:{image:r,width:n,height:o}}}case"screenshotViewport":{let{image:t,width:r,height:n}=await At({viewportOnly:!0}),o=e.uploadUrl,i=e.uploadFields;if(o&&i){let a=atob(t),s=new Uint8Array(a.length);for(let d=0;d<a.length;d++)s[d]=a.charCodeAt(d);let c=new FormData;for(let[d,f]of Object.entries(i))c.append(d,f);c.append("file",new Blob([s],{type:"image/jpeg"}),"screenshot.jpg");let l=await fetch(o,{method:"POST",body:c});if(!l.ok)throw new Error(`Screenshot upload failed: ${l.status}`);let u=e._publicUrl;return{result:{width:r,height:n,uploaded:!0,...u?{_publicUrl:u}:{}}}}return{result:{image:t,width:r,height:n}}}case"reload":return location.reload(),{result:"ok"};default:throw new Error(`Unknown command: ${e.command}`)}}var Qu="/__mindstudio_dev__/ws",ed=1e3,td=1e4;function qi(){let e=A().ws;e.ws||(Bn(ir),window.addEventListener("beforeunload",()=>{e.closing=!0,e.ws&&e.ws.close()}),ji())}function ir(){let{ws:e}=A().ws;return e&&e.readyState===WebSocket.OPEN?e:null}function rd(){return`${location.protocol==="https:"?"wss:":"ws:"}//${location.host}${Qu}`}function nd(){return window.location.search.includes("mode=iframe")?"iframe":"standalone"}function ji(){let e=A().ws;if(!e.closing){try{e.ws=new WebSocket(rd())}catch{Yi();return}e.ws.onopen=()=>{try{let t=(()=>{try{return sessionStorage.getItem("__ms_mirror")==="1"}catch{return!1}})();e.ws.send(JSON.stringify({type:"hello",mode:nd(),url:location.href,viewport:{w:window.innerWidth,h:window.innerHeight},mirror:t}))}catch{e.ws?.close()}},e.ws.onmessage=t=>{let r;try{r=JSON.parse(t.data)}catch{return}switch(r.type){case"ack":e.clientId=r.clientId,e.reconnectDelay=ed,id();break;case"command":od(r.id,r.steps);break;case"broadcast":ad(r.action,r.payload);break}},e.ws.onclose=()=>{e.ws=null,e.clientId=null,e.closing||Yi()},e.ws.onerror=()=>{}}}function Yi(){let e=A().ws;e.closing||(setTimeout(()=>ji(),e.reconnectDelay),e.reconnectDelay=Math.min(e.reconnectDelay*2,td))}async function od(e,t){let r=A().ws;if(r.busy){or({id:e,steps:[],snapshot:"",logs:[],duration:0,error:"Browser agent is busy"});return}r.busy=!0,bn("command-started",{id:e,steps:t.map(n=>n.command)});try{let n=await nr(e,t);or(n),bn("command-completed",{id:e,success:!0})}catch{or({id:e,steps:[],snapshot:"",logs:[],duration:0}),bn("command-completed",{id:e,success:!1})}finally{A().ws.busy=!1}}function or(e){try{let t=ir();t&&t.send(JSON.stringify({type:"result",...e}))}catch{}}async function id(){let e=await Gi();e&&or(e)}function bn(e,t){window.parent!==window&&window.parent.postMessage({channel:"mindstudio-browser-agent",command:e,...t},"*")}function ad(e,t){e==="reload"&&(document.cookie="__ms_auth=; Max-Age=0; Path=/; Secure; SameSite=None",location.href="/")}var Zi="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",sd=200,Xi=`
111
+ `,t.style.cssText=["position: fixed","z-index: 2147483647","pointer-events: none","opacity: 0","transition: opacity 300ms ease, left 600ms cubic-bezier(0.4, 0, 0, 1), top 600ms cubic-bezier(0.4, 0, 0, 1)","will-change: left, top, opacity"].join("; ");let r=document.createElement("div");r.id=pn,r.style.cssText=["position: fixed","top: 0","left: 0","width: 100vw","height: 100vh","background: white","opacity: 0","z-index: 2147483646","pointer-events: none","transition: opacity 600ms ease-out"].join("; "),document.documentElement.appendChild(r),document.documentElement.appendChild(t),e.cameraEl=t,e.flashEl=r}function Bu(e,t){let{cameraEl:r}=A().screenshot;r&&(r.style.left=`${e}px`,r.style.top=`${t}px`,r.style.opacity="1")}function $i(e,t){let{cameraEl:r}=A().screenshot;r&&(r.style.left=`${e}px`,r.style.top=`${t}px`)}async function Uu(){let{flashEl:e}=A().screenshot;e&&(e.style.transition="none",e.style.opacity="0.7",e.offsetWidth,e.style.transition="opacity 600ms ease-out",e.style.opacity="0",await oe(600))}function Hu(){let{cameraEl:e}=A().screenshot;e&&(e.style.opacity="0")}function Vu(){let e=A().screenshot;document.getElementById(mn)?.remove(),document.getElementById(pn)?.remove(),e.cameraEl=null,e.flashEl=null}function Gu(e,t,r){return new Promise(n=>{let o=e.scrollTop,i=t-o;if(Math.abs(i)<1){n();return}let a=Math.abs(i),c=Math.max(r,a/2),l=performance.now();function u(d){let f=d-l,m=Math.min(f/c,1),p=m<.5?4*m*m*m:1-Math.pow(-2*m+2,3)/2;e.scrollTop=o+i*p,m<1?requestAnimationFrame(u):n()}requestAnimationFrame(u)})}async function At(e){let t=A().screenshot;if(t.capturing)throw new Error("Screenshot already in progress");t.capturing=!0,_i();try{return await zu(e)}finally{t.capturing=!1,Oi()}}async function zu(e){await fi();let t=window.innerWidth,r=window.innerHeight;if(e?.viewportOnly)return await qu(t,r);let n=We(),o=n!==document.documentElement,i=n.scrollTop,a=n.style.scrollBehavior;n.style.scrollBehavior="auto",Wu();try{let s=Math.random()<.5?t+30:-60,c=r*.3+Math.random()*r*.4;Bu(s,c),await oe(200);let l=t*.4+Math.random()*t*.15,u=r*.35+Math.random()*r*.15;$i(l,u),await oe(700);let d=[],f=0,m=o?n.clientHeight:r,p=()=>o?n.scrollHeight:Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),g=50,h=p();for(;f<h&&d.length<g;){let x=d.length,N=p();if(N<=0){console.warn("[screenshot] skipping chunk \u2014 page height is 0");break}if(N-f<m*.05&&d.length>0)break;let W=N-m,L=Math.min(f,W);Math.abs(L-n.scrollTop)>1&&(await Gu(n,L,Du),$i(l,u),await oe(Ou)),Uu();let O=n.scrollTop,V=Math.min(m,N-O);if(V<=0){console.warn("[screenshot] skipping chunk \u2014 captureHeight is 0 (page may have shrunk)"),f+=m;continue}let K=o?n.getBoundingClientRect():null;try{let F=await(await j(document.body,{embedFonts:!0,exclude:Wi,plugins:[Pi]})).toCanvas({width:t+1});if(F&&K){let D=F.width/t,_=F.width;if(x===0&&K.top>0){let M=Math.round(K.top*D),G=document.createElement("canvas");G.width=_,G.height=M;let J=G.getContext("2d");J&&(J.drawImage(F,0,0,_,M,0,0,_,M),d.push({y:0,canvas:G}))}let ee=Math.round(K.top*D),$=Math.round(V*D),z=document.createElement("canvas");z.width=_,z.height=$;let X=z.getContext("2d");X&&(X.drawImage(F,0,ee,_,$,0,0,_,$),d.push({y:K.top+O,canvas:z}));let ne=K.bottom;if(f+m>=h&&ne<r){let M=Math.round(ne*D),G=F.height-M;if(G>0){let J=document.createElement("canvas");J.width=_,J.height=G;let Y=J.getContext("2d");Y&&(Y.drawImage(F,0,M,_,G,0,0,_,G),d.push({y:K.top+N,canvas:J}))}}}else if(F){let D=F.width/t,_=Math.round(O*D),ee=Math.min(O+V,N),$=Math.round(ee*D)-_,z=F.width,X=document.createElement("canvas");X.width=z,X.height=$;let ne=X.getContext("2d");ne&&(ne.drawImage(F,0,_,z,$,0,0,z,$),d.push({y:O,canvas:X}))}}catch(R){console.error("[screenshot] snapdom capture failed for chunk",x,R)}f+=m}if(f<h&&console.warn(`[screenshot] page truncated \u2014 captured ${d.length} chunks but page is ${h}px tall`),d.length===0)throw new Error("Screenshot capture produced no image data");let y=d[0].canvas.width,v=0;for(let x of d)v+=x.canvas.height;let w=document.createElement("canvas");w.width=y,w.height=v;let b=w.getContext("2d");if(!b)throw new Error("Failed to create output canvas");let S=0;for(let x of d)b.drawImage(x.canvas,0,S),S+=x.canvas.height;let C=w.toDataURL("image/jpeg",1).replace(/^data:image\/jpeg;base64,/,"");if(!C)throw new Error("Canvas export produced no image data");return{image:C,width:w.width,height:w.height}}finally{Hu(),Vu(),n.style.scrollBehavior="auto",n.scrollTop=i,n.style.scrollBehavior=a}}function Yu(){let e=document.getElementById("__mindstudio-notes-overlay");if(!e)return null;let t=window.scrollX,r=window.scrollY,n=e.style.cssText;return e.style.position="absolute",e.style.inset="auto",e.style.left=`${t}px`,e.style.top=`${r}px`,e.style.width=`${window.innerWidth}px`,e.style.height=`${window.innerHeight}px`,()=>{e.style.cssText=n}}async function qu(e,t){let r=Yu();try{let o=await(await j(document.body,{embedFonts:!0,exclude:Wi,plugins:[Pi]})).toCanvas({width:(e+1)*Ve});if(!o)throw new Error("SnapDOM viewport capture returned empty result");let i=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),a=document.createElement("canvas");a.width=e*Ve,a.height=t*Ve;let s=a.getContext("2d");if(!s)throw new Error("Failed to create canvas context");let c=window.scrollY/i*o.height,l=t/i*o.height;s.drawImage(o,0,c,o.width,l,0,0,e*Ve,t*Ve);let d=a.toDataURL("image/jpeg",1).replace(/^data:image\/jpeg;base64,/,"");if(!d)throw new Error("Canvas export produced no image data");return{image:d,width:(e+1)*Ve,height:t*Ve}}finally{r?.()}}var ju=["backgroundColor","color","fontFamily","fontSize","fontWeight","fontStyle","lineHeight","letterSpacing","textAlign","textDecoration","textTransform","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","borderRadius","borderColor","borderWidth","borderStyle","boxShadow","opacity","width","height","display","flexDirection","justifyContent","alignItems","gap"];function Zu(e,t){let r=t??ju;if(e.ref||e.text||e.role||e.label||e.selector){let{element:i,matched:a}=Me(e);return{...Bi(i,e.ref,r),matched:a}}let n=Je(),o=[];for(let[i,a]of n)a.isConnected&&o.push(Bi(a,i,r));return o}function Bi(e,t,r){let n=window.getComputedStyle(e),o={};t&&(o.ref=t),o.tag=e.tagName.toLowerCase();let i=e.textContent?.trim();i&&(o.text=i.length>60?i.slice(0,60)+"\u2026":i);for(let a of r){let s=n.getPropertyValue(a.replace(/([A-Z])/g,"-$1").toLowerCase());s&&(o[a]=s)}return o}var Ui=150,rr=100,Et="__mindstudio_pending_nav",Hi=300,yn=1e4;function Xu(e){try{let t=new URL(e,window.location.origin);return t.origin!==window.location.origin?!1:t.pathname===window.location.pathname&&t.search===window.location.search&&t.hash===window.location.hash}catch{return!1}}function Vi(e){try{let t=new URL(e,window.location.origin);return t.origin!==window.location.origin?!1:(Xu(e)||(window.history.pushState(null,"",t.pathname+t.search+t.hash),window.dispatchEvent(new PopStateEvent("popstate"))),!0)}catch{return!1}}async function hn(e=300,t=2e3){return new Promise(r=>{let n=Date.now()+t,o=Date.now(),i=We(),a=window.scrollY,s=window.scrollX,c=document.body.scrollTop,l=document.documentElement.scrollTop,u=document.body.scrollHeight,d=document.body.scrollWidth,f=i.scrollTop,m=i.scrollLeft;function p(){if(Date.now()>n){r();return}let g=window.scrollY,h=window.scrollX,y=document.body.scrollTop,v=document.documentElement.scrollTop,w=document.body.scrollHeight,b=document.body.scrollWidth,S=i.scrollTop,I=i.scrollLeft;(g!==a||h!==s||y!==c||v!==l||w!==u||b!==d||S!==f||I!==m)&&(a=g,s=h,c=y,l=v,u=w,d=b,f=S,m=I,o=Date.now()),Date.now()-o>=e?r():requestAnimationFrame(p)}requestAnimationFrame(p)})}var Ku=350;async function gn(e){let t=Ju(e),r=t.map(a=>({el:a,top:a.scrollTop,left:a.scrollLeft})),n=document.documentElement.style.scrollBehavior;document.documentElement.style.scrollBehavior="auto",e.scrollIntoView({block:"center",inline:"nearest",behavior:"instant"}),document.documentElement.style.scrollBehavior=n;let o=t.map(a=>({top:a.scrollTop,left:a.scrollLeft})),i=[];for(let a=0;a<t.length;a++)(r[a].top!==o[a].top||r[a].left!==o[a].left)&&i.push({el:t[a],fromTop:r[a].top,fromLeft:r[a].left,toTop:o[a].top,toLeft:o[a].left});if(i.length===0){await new Promise(a=>requestAnimationFrame(()=>a()));return}for(let a of i)a.el.scrollTop=a.fromTop,a.el.scrollLeft=a.fromLeft;await new Promise(a=>{let s=performance.now();function c(l){let u=l-s,d=Math.min(u/Ku,1),f=1-Math.pow(1-d,3);for(let m of i)m.el.scrollTop=m.fromTop+(m.toTop-m.fromTop)*f,m.el.scrollLeft=m.fromLeft+(m.toLeft-m.fromLeft)*f;d<1?requestAnimationFrame(c):a()}requestAnimationFrame(c)})}function Ju(e){let t=[],r=e.parentElement;for(;r;){let n=getComputedStyle(r),o=n.overflowY,i=n.overflowX;(o==="auto"||o==="scroll"||i==="auto"||i==="scroll")&&t.push(r),r=r.parentElement}return t.push(document.scrollingElement||document.documentElement),t}async function nr(e,t){let r=Date.now(),n=[],o=[],i="",a=!1,s=new Set(["click","type","select"]),c=t.some(l=>s.has(l.command));c&&(It(!0),dn()),Fr();try{for(let l=0;l<t.length;l++){let u=t[l],d=u.command;try{if(l>0&&await oe(Ui+Math.random()*rr-rr/2),d==="navigate"){let m=u.url;if(!m)throw new Error('navigate command requires a "url" field');if(Vi(m)){n.push({index:l,command:d,result:"ok"}),await oe(Hi);continue}let p=t.slice(l+1);if(p.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e,startTime:r,completedSteps:n,remainingSteps:p,stepOffset:l+1}))}catch{}n.push({index:l,command:d,result:"ok"}),a=!0,window.location.href=m,await new Promise((g,h)=>{setTimeout(()=>h(new Error("Navigation did not complete within timeout")),yn)})}if(d==="reload"){let m=t.slice(l+1);if(m.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e,startTime:r,completedSteps:n,remainingSteps:m,stepOffset:l+1}))}catch{}n.push({index:l,command:d,result:"ok"}),a=!0,window.location.reload(),await new Promise((p,g)=>{setTimeout(()=>g(new Error("Reload did not complete within timeout")),yn)})}let f=await zi(u);n.push({index:l,command:d,...f})}catch(f){n.push({index:l,command:d,error:f instanceof Error?f.message:String(f)});break}}try{i=await Qe()}catch{}}finally{o=_r(),c&&(It(!1),er())}return{id:e,steps:n,snapshot:i,logs:o,duration:Date.now()-r}}async function Gi(){let e=null;try{let i=sessionStorage.getItem(Et);if(!i)return null;sessionStorage.removeItem(Et),e=JSON.parse(i)}catch{return null}if(!e)return null;let t=[...e.completedSteps],r=[],n="",o=e.remainingSteps.some(i=>i.command==="click"||i.command==="type"||i.command==="select");o&&(It(!0),dn()),Fr();try{for(let i=0;i<e.remainingSteps.length;i++){let a=e.remainingSteps[i],s=a.command,c=e.stepOffset+i;try{if(i>0&&await oe(Ui+Math.random()*rr-rr/2),s==="navigate"){let u=a.url;if(!u)throw new Error('navigate command requires a "url" field');if(Vi(u)){t.push({index:c,command:s,result:"ok"}),await oe(Hi);continue}let d=e.remainingSteps.slice(i+1);if(d.length>0)try{sessionStorage.setItem(Et,JSON.stringify({id:e.id,startTime:e.startTime,completedSteps:t,remainingSteps:d,stepOffset:c+1}))}catch{}t.push({index:c,command:s,result:"ok"}),window.location.href=u,await new Promise((f,m)=>{setTimeout(()=>m(new Error("Navigation did not complete within timeout")),yn)})}let l=await zi(a);t.push({index:c,command:s,...l})}catch(l){t.push({index:c,command:s,error:l instanceof Error?l.message:String(l)});break}}try{n=await Qe()}catch{}}finally{r=_r(),o&&(It(!1),er())}return{id:e.id,steps:t,snapshot:n,logs:r,duration:Date.now()-e.startTime}}async function zi(e){switch(e.command){case"snapshot":return{result:await Qe()};case"click":{let{element:t,matched:r}=Me(e);return await gn(t),await Qt(t),await Fi(),Wr(t),await hn(),await oe(1e3),{result:"ok",matched:r}}case"type":{let{element:t,matched:r}=Me(e),n=e.text;if(!n&&n!=="")throw new Error('type command requires a "text" field');return await gn(t),await Qt(t),await no(t,n,{clear:!!e.clear}),await hn(),{result:"ok",matched:r}}case"select":{let{element:t,matched:r}=Me(e),n=e.option;if(!n)throw new Error('select command requires an "option" field');await gn(t),await Qt(t);let o=io(t,n);return await hn(),{result:`selected "${o}"`,matched:r}}case"wait":{let{matched:t,elapsed:r}=await oo(e);return{result:"ok",matched:t,elapsed:r}}case"styles":{let t=e.properties;return{result:Zu(e,t)}}case"evaluate":{let t=e.script;if(!t)throw new Error('evaluate command requires a "script" field');return{result:await ao(t)}}case"screenshotFullPage":{let t=We();t.scrollTop=0,await oe(500);let{image:r,width:n,height:o}=await At(),i=e.uploadUrl,a=e.uploadFields;if(i&&a){let s=atob(r),c=new Uint8Array(s.length);for(let f=0;f<s.length;f++)c[f]=s.charCodeAt(f);let l=new FormData;for(let[f,m]of Object.entries(a))l.append(f,m);l.append("file",new Blob([c],{type:"image/jpeg"}),"screenshot.jpg");let u=await fetch(i,{method:"POST",body:l});if(!u.ok)throw new Error(`Screenshot upload failed: ${u.status}`);let d=e._publicUrl;return{result:{width:n,height:o,uploaded:!0,...d?{_publicUrl:d}:{}}}}return{result:{image:r,width:n,height:o}}}case"screenshotViewport":{let{image:t,width:r,height:n}=await At({viewportOnly:!0}),o=e.uploadUrl,i=e.uploadFields;if(o&&i){let a=atob(t),s=new Uint8Array(a.length);for(let d=0;d<a.length;d++)s[d]=a.charCodeAt(d);let c=new FormData;for(let[d,f]of Object.entries(i))c.append(d,f);c.append("file",new Blob([s],{type:"image/jpeg"}),"screenshot.jpg");let l=await fetch(o,{method:"POST",body:c});if(!l.ok)throw new Error(`Screenshot upload failed: ${l.status}`);let u=e._publicUrl;return{result:{width:r,height:n,uploaded:!0,...u?{_publicUrl:u}:{}}}}return{result:{image:t,width:r,height:n}}}case"reload":return location.reload(),{result:"ok"};default:throw new Error(`Unknown command: ${e.command}`)}}var Qu="/__mindstudio_dev__/ws",ed=1e3,td=1e4;function qi(){let e=A().ws;e.ws||(Bn(ir),window.addEventListener("beforeunload",()=>{e.closing=!0,e.ws&&e.ws.close()}),ji())}function ir(){let{ws:e}=A().ws;return e&&e.readyState===WebSocket.OPEN?e:null}function rd(){return`${location.protocol==="https:"?"wss:":"ws:"}//${location.host}${Qu}`}function nd(){return window.location.search.includes("mode=iframe")?"iframe":"standalone"}function ji(){let e=A().ws;if(!e.closing){try{e.ws=new WebSocket(rd())}catch{Yi();return}e.ws.onopen=()=>{try{let t=(()=>{try{return sessionStorage.getItem("__ms_mirror")==="1"}catch{return!1}})();e.ws.send(JSON.stringify({type:"hello",mode:nd(),url:location.href,viewport:{w:window.innerWidth,h:window.innerHeight},mirror:t}))}catch{e.ws?.close()}},e.ws.onmessage=t=>{let r;try{r=JSON.parse(t.data)}catch{return}switch(r.type){case"ack":e.clientId=r.clientId,e.reconnectDelay=ed,id();break;case"command":od(r.id,r.steps);break;case"broadcast":ad(r.action,r.payload);break}},e.ws.onclose=()=>{e.ws=null,e.clientId=null,e.closing||Yi()},e.ws.onerror=()=>{}}}function Yi(){let e=A().ws;e.closing||(setTimeout(()=>ji(),e.reconnectDelay),e.reconnectDelay=Math.min(e.reconnectDelay*2,td))}async function od(e,t){let r=A().ws;if(r.busy){or({id:e,steps:[],snapshot:"",logs:[],duration:0,error:"Browser agent is busy"});return}r.busy=!0,bn("command-started",{id:e,steps:t.map(n=>n.command)});try{let n=await nr(e,t);or(n),bn("command-completed",{id:e,success:!0})}catch{or({id:e,steps:[],snapshot:"",logs:[],duration:0}),bn("command-completed",{id:e,success:!1})}finally{A().ws.busy=!1}}function or(e){try{let t=ir();t&&t.send(JSON.stringify({type:"result",...e}))}catch{}}async function id(){let e=await Gi();e&&or(e)}function bn(e,t){window.parent!==window&&window.parent.postMessage({channel:"mindstudio-browser-agent",command:e,...t},"*")}function ad(e,t){e==="reload"&&(document.cookie="__ms_auth=; Max-Age=0; Path=/; Secure; SameSite=None",location.href="/")}var Zi="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",sd=200,Xi=`
112
112
  all: initial !important;
113
113
  display: block !important;
114
114
  box-sizing: border-box !important;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/browser-agent",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "description": "Browser-side agent for MindStudio dev previews — captures logs, provides DOM snapshots, and enables remote interaction.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -20,7 +20,7 @@
20
20
  "typescript": "^5.7.0"
21
21
  },
22
22
  "dependencies": {
23
- "@zumer/snapdom": "^2.6.0",
23
+ "@zumer/snapdom": "^2.7.0",
24
24
  "rrweb": "^2.0.0-alpha.4"
25
25
  }
26
26
  }