var fg=e=>{throw TypeError(e)};var Ju=(e,t,n)=>t.has(e)||fg("Cannot "+n);var I=(e,t,n)=>(Ju(e,t,"read from private field"),n?n.call(e):t.get(e)),je=(e,t,n)=>t.has(e)?fg("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),oe=(e,t,n,r)=>(Ju(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n),wt=(e,t,n)=>(Ju(e,t,"access private method"),n);var Ll=(e,t,n,r)=>({set _(o){oe(e,t,o,n)},get _(){return I(e,t,r)}});function bC(e,t){for(var n=0;nr[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){if(typeof window>"u")return;const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(o){if(o.ep)return;o.ep=!0;const a=n(o);fetch(o.href,a)}})();function Pw(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ow={exports:{}},nu={},Dw={exports:{}},pe={};/** * @license React * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var kl=Symbol.for("react.element"),wC=Symbol.for("react.portal"),jC=Symbol.for("react.fragment"),NC=Symbol.for("react.strict_mode"),kC=Symbol.for("react.profiler"),_C=Symbol.for("react.provider"),CC=Symbol.for("react.context"),SC=Symbol.for("react.forward_ref"),EC=Symbol.for("react.suspense"),TC=Symbol.for("react.memo"),IC=Symbol.for("react.lazy"),gg=Symbol.iterator;function AC(e){return e===null||typeof e!="object"?null:(e=gg&&e[gg]||e["@@iterator"],typeof e=="function"?e:null)}var Rw={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Mw=Object.assign,$w={};function Ia(e,t,n){this.props=e,this.context=t,this.refs=$w,this.updater=n||Rw}Ia.prototype.isReactComponent={};Ia.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Ia.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Lw(){}Lw.prototype=Ia.prototype;function dx(e,t,n){this.props=e,this.context=t,this.refs=$w,this.updater=n||Rw}var ux=dx.prototype=new Lw;ux.constructor=dx;Mw(ux,Ia.prototype);ux.isPureReactComponent=!0;var vg=Array.isArray,Fw=Object.prototype.hasOwnProperty,hx={current:null},zw={key:!0,ref:!0,__self:!0,__source:!0};function Bw(e,t,n){var r,o={},a=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(a=""+t.key),t)Fw.call(t,r)&&!zw.hasOwnProperty(r)&&(o[r]=t[r]);var l=arguments.length-2;if(l===1)o.children=n;else if(1>>1,W=E[Z];if(0>>1;Zo(we,$))Neo(F,we)?(E[Z]=F,E[Ne]=$,Z=Ne):(E[Z]=we,E[J]=$,Z=J);else if(Neo(F,$))E[Z]=F,E[Ne]=$,Z=Ne;else break e}}return T}function o(E,T){var $=E.sortIndex-T.sortIndex;return $!==0?$:E.id-T.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,l=i.now();e.unstable_now=function(){return i.now()-l}}var c=[],d=[],u=1,h=null,p=3,m=!1,w=!1,x=!1,b=typeof setTimeout=="function"?setTimeout:null,v=typeof clearTimeout=="function"?clearTimeout:null,g=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function y(E){for(var T=n(d);T!==null;){if(T.callback===null)r(d);else if(T.startTime<=E)r(d),T.sortIndex=T.expirationTime,t(c,T);else break;T=n(d)}}function j(E){if(x=!1,y(E),!w)if(n(c)!==null)w=!0,q(N);else{var T=n(d);T!==null&&Y(j,T.startTime-E)}}function N(E,T){w=!1,x&&(x=!1,v(C),C=-1),m=!0;var $=p;try{for(y(T),h=n(c);h!==null&&(!(h.expirationTime>T)||E&&!H());){var Z=h.callback;if(typeof Z=="function"){h.callback=null,p=h.priorityLevel;var W=Z(h.expirationTime<=T);T=e.unstable_now(),typeof W=="function"?h.callback=W:h===n(c)&&r(c),y(T)}else r(c);h=n(c)}if(h!==null)var ee=!0;else{var J=n(d);J!==null&&Y(j,J.startTime-T),ee=!1}return ee}finally{h=null,p=$,m=!1}}var _=!1,k=null,C=-1,P=5,A=-1;function H(){return!(e.unstable_now()-AE||125Z?(E.sortIndex=$,t(d,E),n(c)===null&&E===n(d)&&(x?(v(C),C=-1):x=!0,Y(j,$-Z))):(E.sortIndex=W,t(c,E),w||m||(w=!0,q(N))),E},e.unstable_shouldYield=H,e.unstable_wrapCallback=function(E){var T=p;return function(){var $=p;p=T;try{return E.apply(this,arguments)}finally{p=$}}}})(Vw);qw.exports=Vw;var WC=qw.exports;/** * @license React * react-dom.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var UC=f,cs=WC;function D(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),mm=Object.prototype.hasOwnProperty,HC=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,bg={},wg={};function qC(e){return mm.call(wg,e)?!0:mm.call(bg,e)?!1:HC.test(e)?wg[e]=!0:(bg[e]=!0,!1)}function VC(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function GC(e,t,n,r){if(t===null||typeof t>"u"||VC(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Mt(e,t,n,r,o,a,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=i}var gt={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){gt[e]=new Mt(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];gt[t]=new Mt(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){gt[e]=new Mt(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){gt[e]=new Mt(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){gt[e]=new Mt(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){gt[e]=new Mt(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){gt[e]=new Mt(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){gt[e]=new Mt(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){gt[e]=new Mt(e,5,!1,e.toLowerCase(),null,!1,!1)});var xx=/[\-:]([a-z])/g;function fx(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(xx,fx);gt[t]=new Mt(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(xx,fx);gt[t]=new Mt(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(xx,fx);gt[t]=new Mt(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){gt[e]=new Mt(e,1,!1,e.toLowerCase(),null,!1,!1)});gt.xlinkHref=new Mt("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){gt[e]=new Mt(e,1,!1,e.toLowerCase(),null,!0,!0)});function gx(e,t,n,r){var o=gt.hasOwnProperty(t)?gt[t]:null;(o!==null?o.type!==0:r||!(2l||o[i]!==a[l]){var c=` `+o[i].replace(" at new "," at ");return e.displayName&&c.includes("")&&(c=c.replace("",e.displayName)),c}while(1<=i&&0<=l);break}}}finally{th=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?mi(e):""}function YC(e){switch(e.tag){case 5:return mi(e.type);case 16:return mi("Lazy");case 13:return mi("Suspense");case 19:return mi("SuspenseList");case 0:case 2:case 15:return e=sh(e.type,!1),e;case 11:return e=sh(e.type.render,!1),e;case 1:return e=sh(e.type,!0),e;default:return""}}function gm(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Mo:return"Fragment";case Ro:return"Portal";case pm:return"Profiler";case vx:return"StrictMode";case xm:return"Suspense";case fm:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Qw:return(e.displayName||"Context")+".Consumer";case Yw:return(e._context.displayName||"Context")+".Provider";case yx:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case bx:return t=e.displayName||null,t!==null?t:gm(e.type)||"Memo";case er:t=e._payload,e=e._init;try{return gm(e(t))}catch{}}return null}function QC(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return gm(t);case 8:return t===vx?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Sr(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Xw(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function KC(e){var t=Xw(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var o=n.get,a=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){r=""+i,a.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Bl(e){e._valueTracker||(e._valueTracker=KC(e))}function Jw(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Xw(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Zc(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function vm(e,t){var n=t.checked;return Qe({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Ng(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=Sr(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Zw(e,t){t=t.checked,t!=null&&gx(e,"checked",t,!1)}function ym(e,t){Zw(e,t);var n=Sr(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?bm(e,t.type,n):t.hasOwnProperty("defaultValue")&&bm(e,t.type,Sr(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function kg(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function bm(e,t,n){(t!=="number"||Zc(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var pi=Array.isArray;function Qo(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=Wl.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function qi(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ti={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},XC=["Webkit","ms","Moz","O"];Object.keys(Ti).forEach(function(e){XC.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ti[t]=Ti[e]})});function nj(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ti.hasOwnProperty(e)&&Ti[e]?(""+t).trim():t+"px"}function rj(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,o=nj(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}var JC=Qe({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Nm(e,t){if(t){if(JC[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(D(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(D(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(D(61))}if(t.style!=null&&typeof t.style!="object")throw Error(D(62))}}function km(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var _m=null;function wx(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Cm=null,Ko=null,Xo=null;function Sg(e){if(e=Sl(e)){if(typeof Cm!="function")throw Error(D(280));var t=e.stateNode;t&&(t=lu(t),Cm(e.stateNode,e.type,t))}}function oj(e){Ko?Xo?Xo.push(e):Xo=[e]:Ko=e}function aj(){if(Ko){var e=Ko,t=Xo;if(Xo=Ko=null,Sg(e),t)for(e=0;e>>=0,e===0?32:31-(cS(e)/dS|0)|0}var Ul=64,Hl=4194304;function xi(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function nd(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,o=e.suspendedLanes,a=e.pingedLanes,i=n&268435455;if(i!==0){var l=i&~o;l!==0?r=xi(l):(a&=i,a!==0&&(r=xi(a)))}else i=n&~o,i!==0?r=xi(i):a!==0&&(r=xi(a));if(r===0)return 0;if(t!==0&&t!==r&&!(t&o)&&(o=r&-r,a=t&-t,o>=a||o===16&&(a&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function _l(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-$s(t),e[t]=n}function pS(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Ai),Mg=" ",$g=!1;function Cj(e,t){switch(e){case"keyup":return WS.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Sj(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var $o=!1;function HS(e,t){switch(e){case"compositionend":return Sj(t);case"keypress":return t.which!==32?null:($g=!0,Mg);case"textInput":return e=t.data,e===Mg&&$g?null:e;default:return null}}function qS(e,t){if($o)return e==="compositionend"||!Tx&&Cj(e,t)?(e=kj(),Cc=Cx=cr=null,$o=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Bg(n)}}function Aj(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Aj(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Pj(){for(var e=window,t=Zc();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Zc(e.document)}return t}function Ix(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function eE(e){var t=Pj(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Aj(n.ownerDocument.documentElement,n)){if(r!==null&&Ix(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var o=n.textContent.length,a=Math.min(r.start,o);r=r.end===void 0?a:Math.min(r.end,o),!e.extend&&a>r&&(o=r,r=a,a=o),o=Wg(n,a);var i=Wg(n,r);o&&i&&(e.rangeCount!==1||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(o.node,o.offset),e.removeAllRanges(),a>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Lo=null,Pm=null,Oi=null,Om=!1;function Ug(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Om||Lo==null||Lo!==Zc(r)||(r=Lo,"selectionStart"in r&&Ix(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Oi&&Xi(Oi,r)||(Oi=r,r=ad(Pm,"onSelect"),0Bo||(e.current=Fm[Bo],Fm[Bo]=null,Bo--)}function Re(e,t){Bo++,Fm[Bo]=e.current,e.current=t}var Er={},St=Pr(Er),Yt=Pr(!1),io=Er;function ga(e,t){var n=e.type.contextTypes;if(!n)return Er;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o={},a;for(a in n)o[a]=t[a];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Qt(e){return e=e.childContextTypes,e!=null}function ld(){Be(Yt),Be(St)}function Kg(e,t,n){if(St.current!==Er)throw Error(D(168));Re(St,t),Re(Yt,n)}function Bj(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var o in r)if(!(o in t))throw Error(D(108,QC(e)||"Unknown",o));return Qe({},n,r)}function cd(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Er,io=St.current,Re(St,e),Re(Yt,Yt.current),!0}function Xg(e,t,n){var r=e.stateNode;if(!r)throw Error(D(169));n?(e=Bj(e,t,io),r.__reactInternalMemoizedMergedChildContext=e,Be(Yt),Be(St),Re(St,e)):Be(Yt),Re(Yt,n)}var wn=null,cu=!1,fh=!1;function Wj(e){wn===null?wn=[e]:wn.push(e)}function hE(e){cu=!0,Wj(e)}function Or(){if(!fh&&wn!==null){fh=!0;var e=0,t=Se;try{var n=wn;for(Se=1;e>=i,o-=i,_n=1<<32-$s(t)+o|n<C?(P=k,k=null):P=k.sibling;var A=p(v,k,y[C],j);if(A===null){k===null&&(k=P);break}e&&k&&A.alternate===null&&t(v,k),g=a(A,g,C),_===null?N=A:_.sibling=A,_=A,k=P}if(C===y.length)return n(v,k),qe&&Wr(v,C),N;if(k===null){for(;CC?(P=k,k=null):P=k.sibling;var H=p(v,k,A.value,j);if(H===null){k===null&&(k=P);break}e&&k&&H.alternate===null&&t(v,k),g=a(H,g,C),_===null?N=H:_.sibling=H,_=H,k=P}if(A.done)return n(v,k),qe&&Wr(v,C),N;if(k===null){for(;!A.done;C++,A=y.next())A=h(v,A.value,j),A!==null&&(g=a(A,g,C),_===null?N=A:_.sibling=A,_=A);return qe&&Wr(v,C),N}for(k=r(v,k);!A.done;C++,A=y.next())A=m(k,v,C,A.value,j),A!==null&&(e&&A.alternate!==null&&k.delete(A.key===null?C:A.key),g=a(A,g,C),_===null?N=A:_.sibling=A,_=A);return e&&k.forEach(function(M){return t(v,M)}),qe&&Wr(v,C),N}function b(v,g,y,j){if(typeof y=="object"&&y!==null&&y.type===Mo&&y.key===null&&(y=y.props.children),typeof y=="object"&&y!==null){switch(y.$$typeof){case zl:e:{for(var N=y.key,_=g;_!==null;){if(_.key===N){if(N=y.type,N===Mo){if(_.tag===7){n(v,_.sibling),g=o(_,y.props.children),g.return=v,v=g;break e}}else if(_.elementType===N||typeof N=="object"&&N!==null&&N.$$typeof===er&&ev(N)===_.type){n(v,_.sibling),g=o(_,y.props),g.ref=Za(v,_,y),g.return=v,v=g;break e}n(v,_);break}else t(v,_);_=_.sibling}y.type===Mo?(g=ao(y.props.children,v.mode,j,y.key),g.return=v,v=g):(j=Dc(y.type,y.key,y.props,null,v.mode,j),j.ref=Za(v,g,y),j.return=v,v=j)}return i(v);case Ro:e:{for(_=y.key;g!==null;){if(g.key===_)if(g.tag===4&&g.stateNode.containerInfo===y.containerInfo&&g.stateNode.implementation===y.implementation){n(v,g.sibling),g=o(g,y.children||[]),g.return=v,v=g;break e}else{n(v,g);break}else t(v,g);g=g.sibling}g=kh(y,v.mode,j),g.return=v,v=g}return i(v);case er:return _=y._init,b(v,g,_(y._payload),j)}if(pi(y))return w(v,g,y,j);if(Ya(y))return x(v,g,y,j);Xl(v,y)}return typeof y=="string"&&y!==""||typeof y=="number"?(y=""+y,g!==null&&g.tag===6?(n(v,g.sibling),g=o(g,y),g.return=v,v=g):(n(v,g),g=Nh(y,v.mode,j),g.return=v,v=g),i(v)):n(v,g)}return b}var ya=Vj(!0),Gj=Vj(!1),hd=Pr(null),md=null,Ho=null,Dx=null;function Rx(){Dx=Ho=md=null}function Mx(e){var t=hd.current;Be(hd),e._currentValue=t}function Wm(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Zo(e,t){md=e,Dx=Ho=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(Vt=!0),e.firstContext=null)}function bs(e){var t=e._currentValue;if(Dx!==e)if(e={context:e,memoizedValue:t,next:null},Ho===null){if(md===null)throw Error(D(308));Ho=e,md.dependencies={lanes:0,firstContext:e}}else Ho=Ho.next=e;return t}var Qr=null;function $x(e){Qr===null?Qr=[e]:Qr.push(e)}function Yj(e,t,n,r){var o=t.interleaved;return o===null?(n.next=n,$x(t)):(n.next=o.next,o.next=n),t.interleaved=n,An(e,r)}function An(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var tr=!1;function Lx(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Qj(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function En(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function vr(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,ye&2){var o=r.pending;return o===null?t.next=t:(t.next=o.next,o.next=t),r.pending=t,An(e,n)}return o=r.interleaved,o===null?(t.next=t,$x(r)):(t.next=o.next,o.next=t),r.interleaved=t,An(e,n)}function Ec(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Nx(e,n)}}function tv(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var o=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};a===null?o=a=i:a=a.next=i,n=n.next}while(n!==null);a===null?o=a=t:a=a.next=t}else o=a=t;n={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:a,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function pd(e,t,n,r){var o=e.updateQueue;tr=!1;var a=o.firstBaseUpdate,i=o.lastBaseUpdate,l=o.shared.pending;if(l!==null){o.shared.pending=null;var c=l,d=c.next;c.next=null,i===null?a=d:i.next=d,i=c;var u=e.alternate;u!==null&&(u=u.updateQueue,l=u.lastBaseUpdate,l!==i&&(l===null?u.firstBaseUpdate=d:l.next=d,u.lastBaseUpdate=c))}if(a!==null){var h=o.baseState;i=0,u=d=c=null,l=a;do{var p=l.lane,m=l.eventTime;if((r&p)===p){u!==null&&(u=u.next={eventTime:m,lane:0,tag:l.tag,payload:l.payload,callback:l.callback,next:null});e:{var w=e,x=l;switch(p=t,m=n,x.tag){case 1:if(w=x.payload,typeof w=="function"){h=w.call(m,h,p);break e}h=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=x.payload,p=typeof w=="function"?w.call(m,h,p):w,p==null)break e;h=Qe({},h,p);break e;case 2:tr=!0}}l.callback!==null&&l.lane!==0&&(e.flags|=64,p=o.effects,p===null?o.effects=[l]:p.push(l))}else m={eventTime:m,lane:p,tag:l.tag,payload:l.payload,callback:l.callback,next:null},u===null?(d=u=m,c=h):u=u.next=m,i|=p;if(l=l.next,l===null){if(l=o.shared.pending,l===null)break;p=l,l=p.next,p.next=null,o.lastBaseUpdate=p,o.shared.pending=null}}while(!0);if(u===null&&(c=h),o.baseState=c,o.firstBaseUpdate=d,o.lastBaseUpdate=u,t=o.shared.interleaved,t!==null){o=t;do i|=o.lane,o=o.next;while(o!==t)}else a===null&&(o.shared.lanes=0);uo|=i,e.lanes=i,e.memoizedState=h}}function sv(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=vh.transition;vh.transition={};try{e(!1),t()}finally{Se=n,vh.transition=r}}function h0(){return ws().memoizedState}function fE(e,t,n){var r=br(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},m0(e))p0(t,n);else if(n=Yj(e,t,n,r),n!==null){var o=Dt();Ls(n,e,r,o),x0(n,t,r)}}function gE(e,t,n){var r=br(e),o={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(m0(e))p0(t,o);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var i=t.lastRenderedState,l=a(i,n);if(o.hasEagerState=!0,o.eagerState=l,zs(l,i)){var c=t.interleaved;c===null?(o.next=o,$x(t)):(o.next=c.next,c.next=o),t.interleaved=o;return}}catch{}finally{}n=Yj(e,t,o,r),n!==null&&(o=Dt(),Ls(n,e,r,o),x0(n,t,r))}}function m0(e){var t=e.alternate;return e===Ye||t!==null&&t===Ye}function p0(e,t){Di=fd=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function x0(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Nx(e,n)}}var gd={readContext:bs,useCallback:jt,useContext:jt,useEffect:jt,useImperativeHandle:jt,useInsertionEffect:jt,useLayoutEffect:jt,useMemo:jt,useReducer:jt,useRef:jt,useState:jt,useDebugValue:jt,useDeferredValue:jt,useTransition:jt,useMutableSource:jt,useSyncExternalStore:jt,useId:jt,unstable_isNewReconciler:!1},vE={readContext:bs,useCallback:function(e,t){return Ks().memoizedState=[e,t===void 0?null:t],e},useContext:bs,useEffect:rv,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ic(4194308,4,i0.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ic(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ic(4,2,e,t)},useMemo:function(e,t){var n=Ks();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ks();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=fE.bind(null,Ye,e),[r.memoizedState,e]},useRef:function(e){var t=Ks();return e={current:e},t.memoizedState=e},useState:nv,useDebugValue:Vx,useDeferredValue:function(e){return Ks().memoizedState=e},useTransition:function(){var e=nv(!1),t=e[0];return e=xE.bind(null,e[1]),Ks().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Ye,o=Ks();if(qe){if(n===void 0)throw Error(D(407));n=n()}else{if(n=t(),dt===null)throw Error(D(349));co&30||Zj(r,t,n)}o.memoizedState=n;var a={value:n,getSnapshot:t};return o.queue=a,rv(t0.bind(null,r,a,e),[e]),r.flags|=2048,ol(9,e0.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=Ks(),t=dt.identifierPrefix;if(qe){var n=Cn,r=_n;n=(r&~(1<<32-$s(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=nl++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[en]=t,e[el]=r,_0(e,t,!1,!1),t.stateNode=e;e:{switch(i=km(n,r),n){case"dialog":Le("cancel",e),Le("close",e),o=r;break;case"iframe":case"object":case"embed":Le("load",e),o=r;break;case"video":case"audio":for(o=0;oja&&(t.flags|=128,r=!0,ei(a,!1),t.lanes=4194304)}else{if(!r)if(e=xd(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),ei(a,!0),a.tail===null&&a.tailMode==="hidden"&&!i.alternate&&!qe)return Nt(t),null}else 2*Ze()-a.renderingStartTime>ja&&n!==1073741824&&(t.flags|=128,r=!0,ei(a,!1),t.lanes=4194304);a.isBackwards?(i.sibling=t.child,t.child=i):(n=a.last,n!==null?n.sibling=i:t.child=i,a.last=i)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=Ze(),t.sibling=null,n=Ve.current,Re(Ve,r?n&1|2:n&1),t):(Nt(t),null);case 22:case 23:return Jx(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?Zt&1073741824&&(Nt(t),t.subtreeFlags&6&&(t.flags|=8192)):Nt(t),null;case 24:return null;case 25:return null}throw Error(D(156,t.tag))}function CE(e,t){switch(Px(t),t.tag){case 1:return Qt(t.type)&&ld(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return ba(),Be(Yt),Be(St),Bx(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return zx(t),null;case 13:if(Be(Ve),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(D(340));va()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Be(Ve),null;case 4:return ba(),null;case 10:return Mx(t.type._context),null;case 22:case 23:return Jx(),null;case 24:return null;default:return null}}var Zl=!1,Ct=!1,SE=typeof WeakSet=="function"?WeakSet:Set,V=null;function qo(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Xe(e,t,r)}else n.current=null}function Xm(e,t,n){try{n()}catch(r){Xe(e,t,r)}}var xv=!1;function EE(e,t){if(Dm=rd,e=Pj(),Ix(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var o=r.anchorOffset,a=r.focusNode;r=r.focusOffset;try{n.nodeType,a.nodeType}catch{n=null;break e}var i=0,l=-1,c=-1,d=0,u=0,h=e,p=null;t:for(;;){for(var m;h!==n||o!==0&&h.nodeType!==3||(l=i+o),h!==a||r!==0&&h.nodeType!==3||(c=i+r),h.nodeType===3&&(i+=h.nodeValue.length),(m=h.firstChild)!==null;)p=h,h=m;for(;;){if(h===e)break t;if(p===n&&++d===o&&(l=i),p===a&&++u===r&&(c=i),(m=h.nextSibling)!==null)break;h=p,p=h.parentNode}h=m}n=l===-1||c===-1?null:{start:l,end:c}}else n=null}n=n||{start:0,end:0}}else n=null;for(Rm={focusedElem:e,selectionRange:n},rd=!1,V=t;V!==null;)if(t=V,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,V=e;else for(;V!==null;){t=V;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var x=w.memoizedProps,b=w.memoizedState,v=t.stateNode,g=v.getSnapshotBeforeUpdate(t.elementType===t.type?x:As(t.type,x),b);v.__reactInternalSnapshotBeforeUpdate=g}break;case 3:var y=t.stateNode.containerInfo;y.nodeType===1?y.textContent="":y.nodeType===9&&y.documentElement&&y.removeChild(y.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(D(163))}}catch(j){Xe(t,t.return,j)}if(e=t.sibling,e!==null){e.return=t.return,V=e;break}V=t.return}return w=xv,xv=!1,w}function Ri(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var o=r=r.next;do{if((o.tag&e)===e){var a=o.destroy;o.destroy=void 0,a!==void 0&&Xm(t,n,a)}o=o.next}while(o!==r)}}function hu(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Jm(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function E0(e){var t=e.alternate;t!==null&&(e.alternate=null,E0(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[en],delete t[el],delete t[Lm],delete t[dE],delete t[uE])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function T0(e){return e.tag===5||e.tag===3||e.tag===4}function fv(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||T0(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Zm(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=id));else if(r!==4&&(e=e.child,e!==null))for(Zm(e,t,n),e=e.sibling;e!==null;)Zm(e,t,n),e=e.sibling}function ep(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(ep(e,t,n),e=e.sibling;e!==null;)ep(e,t,n),e=e.sibling}var ht=null,Rs=!1;function Hn(e,t,n){for(n=n.child;n!==null;)I0(e,t,n),n=n.sibling}function I0(e,t,n){if(on&&typeof on.onCommitFiberUnmount=="function")try{on.onCommitFiberUnmount(ru,n)}catch{}switch(n.tag){case 5:Ct||qo(n,t);case 6:var r=ht,o=Rs;ht=null,Hn(e,t,n),ht=r,Rs=o,ht!==null&&(Rs?(e=ht,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):ht.removeChild(n.stateNode));break;case 18:ht!==null&&(Rs?(e=ht,n=n.stateNode,e.nodeType===8?xh(e.parentNode,n):e.nodeType===1&&xh(e,n),Qi(e)):xh(ht,n.stateNode));break;case 4:r=ht,o=Rs,ht=n.stateNode.containerInfo,Rs=!0,Hn(e,t,n),ht=r,Rs=o;break;case 0:case 11:case 14:case 15:if(!Ct&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){o=r=r.next;do{var a=o,i=a.destroy;a=a.tag,i!==void 0&&(a&2||a&4)&&Xm(n,t,i),o=o.next}while(o!==r)}Hn(e,t,n);break;case 1:if(!Ct&&(qo(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(l){Xe(n,t,l)}Hn(e,t,n);break;case 21:Hn(e,t,n);break;case 22:n.mode&1?(Ct=(r=Ct)||n.memoizedState!==null,Hn(e,t,n),Ct=r):Hn(e,t,n);break;default:Hn(e,t,n)}}function gv(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new SE),t.forEach(function(r){var o=$E.bind(null,e,r);n.has(r)||(n.add(r),r.then(o,o))})}}function Ss(e,t){var n=t.deletions;if(n!==null)for(var r=0;ro&&(o=i),r&=~a}if(r=o,r=Ze()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*IE(r/1960))-r,10e?16:e,dr===null)var r=!1;else{if(e=dr,dr=null,bd=0,ye&6)throw Error(D(331));var o=ye;for(ye|=4,V=e.current;V!==null;){var a=V,i=a.child;if(V.flags&16){var l=a.deletions;if(l!==null){for(var c=0;cZe()-Kx?oo(e,0):Qx|=n),Kt(e,t)}function L0(e,t){t===0&&(e.mode&1?(t=Hl,Hl<<=1,!(Hl&130023424)&&(Hl=4194304)):t=1);var n=Dt();e=An(e,t),e!==null&&(_l(e,t,n),Kt(e,n))}function ME(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),L0(e,n)}function $E(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;o!==null&&(n=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(D(314))}r!==null&&r.delete(t),L0(e,n)}var F0;F0=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Yt.current)Vt=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return Vt=!1,kE(e,t,n);Vt=!!(e.flags&131072)}else Vt=!1,qe&&t.flags&1048576&&Uj(t,ud,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ac(e,t),e=t.pendingProps;var o=ga(t,St.current);Zo(t,n),o=Ux(null,t,r,e,o,n);var a=Hx();return t.flags|=1,typeof o=="object"&&o!==null&&typeof o.render=="function"&&o.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Qt(r)?(a=!0,cd(t)):a=!1,t.memoizedState=o.state!==null&&o.state!==void 0?o.state:null,Lx(t),o.updater=uu,t.stateNode=o,o._reactInternals=t,Hm(t,r,e,n),t=Gm(null,t,r,!0,a,n)):(t.tag=0,qe&&a&&Ax(t),At(null,t,o,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ac(e,t),e=t.pendingProps,o=r._init,r=o(r._payload),t.type=r,o=t.tag=FE(r),e=As(r,e),o){case 0:t=Vm(null,t,r,e,n);break e;case 1:t=hv(null,t,r,e,n);break e;case 11:t=dv(null,t,r,e,n);break e;case 14:t=uv(null,t,r,As(r.type,e),n);break e}throw Error(D(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:As(r,o),Vm(e,t,r,o,n);case 1:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:As(r,o),hv(e,t,r,o,n);case 3:e:{if(j0(t),e===null)throw Error(D(387));r=t.pendingProps,a=t.memoizedState,o=a.element,Qj(e,t),pd(t,r,null,n);var i=t.memoizedState;if(r=i.element,a.isDehydrated)if(a={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=a,t.memoizedState=a,t.flags&256){o=wa(Error(D(423)),t),t=mv(e,t,r,n,o);break e}else if(r!==o){o=wa(Error(D(424)),t),t=mv(e,t,r,n,o);break e}else for(os=gr(t.stateNode.containerInfo.firstChild),is=t,qe=!0,Ms=null,n=Gj(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(va(),r===o){t=Pn(e,t,n);break e}At(e,t,r,n)}t=t.child}return t;case 5:return Kj(t),e===null&&Bm(t),r=t.type,o=t.pendingProps,a=e!==null?e.memoizedProps:null,i=o.children,Mm(r,o)?i=null:a!==null&&Mm(r,a)&&(t.flags|=32),w0(e,t),At(e,t,i,n),t.child;case 6:return e===null&&Bm(t),null;case 13:return N0(e,t,n);case 4:return Fx(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=ya(t,null,r,n):At(e,t,r,n),t.child;case 11:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:As(r,o),dv(e,t,r,o,n);case 7:return At(e,t,t.pendingProps,n),t.child;case 8:return At(e,t,t.pendingProps.children,n),t.child;case 12:return At(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,a=t.memoizedProps,i=o.value,Re(hd,r._currentValue),r._currentValue=i,a!==null)if(zs(a.value,i)){if(a.children===o.children&&!Yt.current){t=Pn(e,t,n);break e}}else for(a=t.child,a!==null&&(a.return=t);a!==null;){var l=a.dependencies;if(l!==null){i=a.child;for(var c=l.firstContext;c!==null;){if(c.context===r){if(a.tag===1){c=En(-1,n&-n),c.tag=2;var d=a.updateQueue;if(d!==null){d=d.shared;var u=d.pending;u===null?c.next=c:(c.next=u.next,u.next=c),d.pending=c}}a.lanes|=n,c=a.alternate,c!==null&&(c.lanes|=n),Wm(a.return,n,t),l.lanes|=n;break}c=c.next}}else if(a.tag===10)i=a.type===t.type?null:a.child;else if(a.tag===18){if(i=a.return,i===null)throw Error(D(341));i.lanes|=n,l=i.alternate,l!==null&&(l.lanes|=n),Wm(i,n,t),i=a.sibling}else i=a.child;if(i!==null)i.return=a;else for(i=a;i!==null;){if(i===t){i=null;break}if(a=i.sibling,a!==null){a.return=i.return,i=a;break}i=i.return}a=i}At(e,t,o.children,n),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,Zo(t,n),o=bs(o),r=r(o),t.flags|=1,At(e,t,r,n),t.child;case 14:return r=t.type,o=As(r,t.pendingProps),o=As(r.type,o),uv(e,t,r,o,n);case 15:return y0(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:As(r,o),Ac(e,t),t.tag=1,Qt(r)?(e=!0,cd(t)):e=!1,Zo(t,n),f0(t,r,o),Hm(t,r,o,n),Gm(null,t,r,!0,e,n);case 19:return k0(e,t,n);case 22:return b0(e,t,n)}throw Error(D(156,t.tag))};function z0(e,t){return mj(e,t)}function LE(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function fs(e,t,n,r){return new LE(e,t,n,r)}function ef(e){return e=e.prototype,!(!e||!e.isReactComponent)}function FE(e){if(typeof e=="function")return ef(e)?1:0;if(e!=null){if(e=e.$$typeof,e===yx)return 11;if(e===bx)return 14}return 2}function wr(e,t){var n=e.alternate;return n===null?(n=fs(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Dc(e,t,n,r,o,a){var i=2;if(r=e,typeof e=="function")ef(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Mo:return ao(n.children,o,a,t);case vx:i=8,o|=8;break;case pm:return e=fs(12,n,t,o|2),e.elementType=pm,e.lanes=a,e;case xm:return e=fs(13,n,t,o),e.elementType=xm,e.lanes=a,e;case fm:return e=fs(19,n,t,o),e.elementType=fm,e.lanes=a,e;case Kw:return pu(n,o,a,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Yw:i=10;break e;case Qw:i=9;break e;case yx:i=11;break e;case bx:i=14;break e;case er:i=16,r=null;break e}throw Error(D(130,e==null?e:typeof e,""))}return t=fs(i,n,t,o),t.elementType=e,t.type=r,t.lanes=a,t}function ao(e,t,n,r){return e=fs(7,e,r,t),e.lanes=n,e}function pu(e,t,n,r){return e=fs(22,e,r,t),e.elementType=Kw,e.lanes=n,e.stateNode={isHidden:!1},e}function Nh(e,t,n){return e=fs(6,e,null,t),e.lanes=n,e}function kh(e,t,n){return t=fs(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function zE(e,t,n,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=rh(0),this.expirationTimes=rh(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=rh(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function tf(e,t,n,r,o,a,i,l,c){return e=new zE(e,t,n,l,c),t===1?(t=1,a===!0&&(t|=8)):t=0,a=fs(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Lx(a),e}function BE(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(H0)}catch(e){console.error(e)}}H0(),Hw.exports=ds;var Oa=Hw.exports;const q0=Pw(Oa);/** * @remix-run/router v1.23.0 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function il(){return il=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function V0(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function GE(){return Math.random().toString(36).substr(2,8)}function Cv(e,t){return{usr:e.state,key:e.key,idx:t}}function op(e,t,n,r){return n===void 0&&(n=null),il({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?wo(t):t,{state:n,key:t&&t.key||r||GE()})}function Na(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function wo(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function YE(e,t,n,r){r===void 0&&(r={});let{window:o=document.defaultView,v5Compat:a=!1}=r,i=o.history,l=Sn.Pop,c=null,d=u();d==null&&(d=0,i.replaceState(il({},i.state,{idx:d}),""));function u(){return(i.state||{idx:null}).idx}function h(){l=Sn.Pop;let b=u(),v=b==null?null:b-d;d=b,c&&c({action:l,location:x.location,delta:v})}function p(b,v){l=Sn.Push;let g=op(x.location,b,v);d=u()+1;let y=Cv(g,d),j=x.createHref(g);try{i.pushState(y,"",j)}catch(N){if(N instanceof DOMException&&N.name==="DataCloneError")throw N;o.location.assign(j)}a&&c&&c({action:l,location:x.location,delta:1})}function m(b,v){l=Sn.Replace;let g=op(x.location,b,v);d=u();let y=Cv(g,d),j=x.createHref(g);i.replaceState(y,"",j),a&&c&&c({action:l,location:x.location,delta:0})}function w(b){let v=o.location.origin!=="null"?o.location.origin:o.location.href,g=typeof b=="string"?b:Na(b);return g=g.replace(/ $/,"%20"),et(v,"No window.location.(origin|href) available to create URL for href: "+g),new URL(g,v)}let x={get action(){return l},get location(){return e(o,i)},listen(b){if(c)throw new Error("A history only accepts one active listener");return o.addEventListener(_v,h),c=b,()=>{o.removeEventListener(_v,h),c=null}},createHref(b){return t(o,b)},createURL:w,encodeLocation(b){let v=w(b);return{pathname:v.pathname,search:v.search,hash:v.hash}},push:p,replace:m,go(b){return i.go(b)}};return x}var Sv;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(Sv||(Sv={}));function QE(e,t,n){return n===void 0&&(n="/"),KE(e,t,n,!1)}function KE(e,t,n,r){let o=typeof t=="string"?wo(t):t,a=of(o.pathname||"/",n);if(a==null)return null;let i=G0(e);XE(i);let l=null;for(let c=0;l==null&&c{let c={relativePath:l===void 0?a.path||"":l,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};c.relativePath.startsWith("/")&&(et(c.relativePath.startsWith(r),'Absolute route path "'+c.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),c.relativePath=c.relativePath.slice(r.length));let d=jr([r,c.relativePath]),u=n.concat(c);a.children&&a.children.length>0&&(et(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+d+'".')),G0(a.children,t,u,d)),!(a.path==null&&!a.index)&&t.push({path:d,score:r3(d,a.index),routesMeta:u})};return e.forEach((a,i)=>{var l;if(a.path===""||!((l=a.path)!=null&&l.includes("?")))o(a,i);else for(let c of Y0(a.path))o(a,i,c)}),t}function Y0(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,o=n.endsWith("?"),a=n.replace(/\?$/,"");if(r.length===0)return o?[a,""]:[a];let i=Y0(r.join("/")),l=[];return l.push(...i.map(c=>c===""?a:[a,c].join("/"))),o&&l.push(...i),l.map(c=>e.startsWith("/")&&c===""?"/":c)}function XE(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:o3(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const JE=/^:[\w-]+$/,ZE=3,e3=2,t3=1,s3=10,n3=-2,Ev=e=>e==="*";function r3(e,t){let n=e.split("/"),r=n.length;return n.some(Ev)&&(r+=n3),t&&(r+=e3),n.filter(o=>!Ev(o)).reduce((o,a)=>o+(JE.test(a)?ZE:a===""?t3:s3),r)}function o3(e,t){return e.length===t.length&&e.slice(0,-1).every((r,o)=>r===t[o])?e[e.length-1]-t[t.length-1]:0}function a3(e,t,n){let{routesMeta:r}=e,o={},a="/",i=[];for(let l=0;l{let{paramName:p,isOptional:m}=u;if(p==="*"){let x=l[h]||"";i=a.slice(0,a.length-x.length).replace(/(.)\/+$/,"$1")}const w=l[h];return m&&!w?d[p]=void 0:d[p]=(w||"").replace(/%2F/g,"/"),d},{}),pathname:a,pathnameBase:i,pattern:e}}function i3(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),V0(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],o="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(i,l,c)=>(r.push({paramName:l,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),o+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?o+="\\/*$":e!==""&&e!=="/"&&(o+="(?:(?=\\/|$))"),[new RegExp(o,t?void 0:"i"),r]}function l3(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return V0(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function of(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function c3(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:o=""}=typeof e=="string"?wo(e):e;return{pathname:n?n.startsWith("/")?n:d3(n,t):t,search:m3(r),hash:p3(o)}}function d3(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(o=>{o===".."?n.length>1&&n.pop():o!=="."&&n.push(o)}),n.length>1?n.join("/"):"/"}function _h(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function u3(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function af(e,t){let n=u3(e);return t?n.map((r,o)=>o===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function lf(e,t,n,r){r===void 0&&(r=!1);let o;typeof e=="string"?o=wo(e):(o=il({},e),et(!o.pathname||!o.pathname.includes("?"),_h("?","pathname","search",o)),et(!o.pathname||!o.pathname.includes("#"),_h("#","pathname","hash",o)),et(!o.search||!o.search.includes("#"),_h("#","search","hash",o)));let a=e===""||o.pathname==="",i=a?"/":o.pathname,l;if(i==null)l=n;else{let h=t.length-1;if(!r&&i.startsWith("..")){let p=i.split("/");for(;p[0]==="..";)p.shift(),h-=1;o.pathname=p.join("/")}l=h>=0?t[h]:"/"}let c=c3(o,l),d=i&&i!=="/"&&i.endsWith("/"),u=(a||i===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||u)&&(c.pathname+="/"),c}const jr=e=>e.join("/").replace(/\/\/+/g,"/"),h3=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),m3=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,p3=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function x3(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Q0=["post","put","patch","delete"];new Set(Q0);const f3=["get",...Q0];new Set(f3);/** * React Router v6.30.1 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function ll(){return ll=Object.assign?Object.assign.bind():function(e){for(var t=1;t{l.current=!0}),f.useCallback(function(d,u){if(u===void 0&&(u={}),!l.current)return;if(typeof d=="number"){r.go(d);return}let h=lf(d,JSON.parse(i),a,u.relative==="path");e==null&&t!=="/"&&(h.pathname=h.pathname==="/"?t:jr([t,h.pathname])),(u.replace?r.replace:r.push)(h,u.state,u)},[t,r,i,a,e])}function t$(){let{matches:e}=f.useContext(Ln),t=e[e.length-1];return t?t.params:{}}function J0(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=f.useContext(Dr),{matches:o}=f.useContext(Ln),{pathname:a}=jo(),i=JSON.stringify(af(o,r.v7_relativeSplatPath));return f.useMemo(()=>lf(e,JSON.parse(i),a,n==="path"),[e,i,a,n])}function b3(e,t){return w3(e,t)}function w3(e,t,n,r){Da()||et(!1);let{navigator:o}=f.useContext(Dr),{matches:a}=f.useContext(Ln),i=a[a.length-1],l=i?i.params:{};i&&i.pathname;let c=i?i.pathnameBase:"/";i&&i.route;let d=jo(),u;if(t){var h;let b=typeof t=="string"?wo(t):t;c==="/"||(h=b.pathname)!=null&&h.startsWith(c)||et(!1),u=b}else u=d;let p=u.pathname||"/",m=p;if(c!=="/"){let b=c.replace(/^\//,"").split("/");m="/"+p.replace(/^\//,"").split("/").slice(b.length).join("/")}let w=QE(e,{pathname:m}),x=C3(w&&w.map(b=>Object.assign({},b,{params:Object.assign({},l,b.params),pathname:jr([c,o.encodeLocation?o.encodeLocation(b.pathname).pathname:b.pathname]),pathnameBase:b.pathnameBase==="/"?c:jr([c,o.encodeLocation?o.encodeLocation(b.pathnameBase).pathname:b.pathnameBase])})),a,n,r);return t&&x?f.createElement(yu.Provider,{value:{location:ll({pathname:"/",search:"",hash:"",state:null,key:"default"},u),navigationType:Sn.Pop}},x):x}function j3(){let e=I3(),t=x3(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,o={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return f.createElement(f.Fragment,null,f.createElement("h2",null,"Unexpected Application Error!"),f.createElement("h3",{style:{fontStyle:"italic"}},t),n?f.createElement("pre",{style:o},n):null,null)}const N3=f.createElement(j3,null);class k3 extends f.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?f.createElement(Ln.Provider,{value:this.props.routeContext},f.createElement(K0.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function _3(e){let{routeContext:t,match:n,children:r}=e,o=f.useContext(cf);return o&&o.static&&o.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=n.route.id),f.createElement(Ln.Provider,{value:t},r)}function C3(e,t,n,r){var o;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var a;if(!n)return null;if(n.errors)e=n.matches;else if((a=r)!=null&&a.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let i=e,l=(o=n)==null?void 0:o.errors;if(l!=null){let u=i.findIndex(h=>h.route.id&&(l==null?void 0:l[h.route.id])!==void 0);u>=0||et(!1),i=i.slice(0,Math.min(i.length,u+1))}let c=!1,d=-1;if(n&&r&&r.v7_partialHydration)for(let u=0;u=0?i=i.slice(0,d+1):i=[i[0]];break}}}return i.reduceRight((u,h,p)=>{let m,w=!1,x=null,b=null;n&&(m=l&&h.route.id?l[h.route.id]:void 0,x=h.route.errorElement||N3,c&&(d<0&&p===0?(w=!0,b=null):d===p&&(w=!0,b=h.route.hydrateFallbackElement||null)));let v=t.concat(i.slice(0,p+1)),g=()=>{let y;return m?y=x:w?y=b:h.route.Component?y=f.createElement(h.route.Component,null):h.route.element?y=h.route.element:y=u,f.createElement(_3,{match:h,routeContext:{outlet:u,matches:v,isDataRoute:n!=null},children:y})};return n&&(h.route.ErrorBoundary||h.route.errorElement||p===0)?f.createElement(k3,{location:n.location,revalidation:n.revalidation,component:x,error:m,children:g(),routeContext:{outlet:null,matches:v,isDataRoute:!0}}):g()},null)}var Z0=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(Z0||{}),Nd=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(Nd||{});function S3(e){let t=f.useContext(cf);return t||et(!1),t}function E3(e){let t=f.useContext(g3);return t||et(!1),t}function T3(e){let t=f.useContext(Ln);return t||et(!1),t}function eN(e){let t=T3(),n=t.matches[t.matches.length-1];return n.route.id||et(!1),n.route.id}function I3(){var e;let t=f.useContext(K0),n=E3(Nd.UseRouteError),r=eN(Nd.UseRouteError);return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function A3(){let{router:e}=S3(Z0.UseNavigateStable),t=eN(Nd.UseNavigateStable),n=f.useRef(!1);return X0(()=>{n.current=!0}),f.useCallback(function(o,a){a===void 0&&(a={}),n.current&&(typeof o=="number"?e.navigate(o):e.navigate(o,ll({fromRouteId:t},a)))},[e,t])}function P3(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function ap(e){let{to:t,replace:n,state:r,relative:o}=e;Da()||et(!1);let{future:a,static:i}=f.useContext(Dr),{matches:l}=f.useContext(Ln),{pathname:c}=jo(),d=Fn(),u=lf(t,af(l,a.v7_relativeSplatPath),c,o==="path"),h=JSON.stringify(u);return f.useEffect(()=>d(JSON.parse(h),{replace:n,state:r,relative:o}),[d,h,o,n,r]),null}function tN(e){et(!1)}function sN(e){let{basename:t="/",children:n=null,location:r,navigationType:o=Sn.Pop,navigator:a,static:i=!1,future:l}=e;Da()&&et(!1);let c=t.replace(/^\/*/,"/"),d=f.useMemo(()=>({basename:c,navigator:a,static:i,future:ll({v7_relativeSplatPath:!1},l)}),[c,l,a,i]);typeof r=="string"&&(r=wo(r));let{pathname:u="/",search:h="",hash:p="",state:m=null,key:w="default"}=r,x=f.useMemo(()=>{let b=of(u,c);return b==null?null:{location:{pathname:b,search:h,hash:p,state:m,key:w},navigationType:o}},[c,u,h,p,m,w,o]);return x==null?null:f.createElement(Dr.Provider,{value:d},f.createElement(yu.Provider,{children:n,value:x}))}function O3(e){let{children:t,location:n}=e;return b3(ip(t),n)}new Promise(()=>{});function ip(e,t){t===void 0&&(t=[]);let n=[];return f.Children.forEach(e,(r,o)=>{if(!f.isValidElement(r))return;let a=[...t,o];if(r.type===f.Fragment){n.push.apply(n,ip(r.props.children,a));return}r.type!==tN&&et(!1),!r.props.index||!r.props.children||et(!1);let i={id:r.props.id||a.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(i.children=ip(r.props.children,a)),n.push(i)}),n}/** * React Router DOM v6.30.1 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function lp(){return lp=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[o]=e[o]);return n}function R3(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function M3(e,t){return e.button===0&&(!t||t==="_self")&&!R3(e)}function cp(e){return e===void 0&&(e=""),new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,n)=>{let r=e[n];return t.concat(Array.isArray(r)?r.map(o=>[n,o]):[[n,r]])},[]))}function $3(e,t){let n=cp(e);return t&&t.forEach((r,o)=>{n.has(o)||t.getAll(o).forEach(a=>{n.append(o,a)})}),n}const L3=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],F3="6";try{window.__reactRouterVersion=F3}catch{}const z3="startTransition",Iv=px[z3];function s$(e){let{basename:t,children:n,future:r,window:o}=e,a=f.useRef();a.current==null&&(a.current=VE({window:o,v5Compat:!0}));let i=a.current,[l,c]=f.useState({action:i.action,location:i.location}),{v7_startTransition:d}=r||{},u=f.useCallback(h=>{d&&Iv?Iv(()=>c(h)):c(h)},[c,d]);return f.useLayoutEffect(()=>i.listen(u),[i,u]),f.useEffect(()=>P3(r),[r]),f.createElement(sN,{basename:t,children:n,location:l.location,navigationType:l.action,navigator:i,future:r})}const B3=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",W3=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Fs=f.forwardRef(function(t,n){let{onClick:r,relative:o,reloadDocument:a,replace:i,state:l,target:c,to:d,preventScrollReset:u,viewTransition:h}=t,p=D3(t,L3),{basename:m}=f.useContext(Dr),w,x=!1;if(typeof d=="string"&&W3.test(d)&&(w=d,B3))try{let y=new URL(window.location.href),j=d.startsWith("//")?new URL(y.protocol+d):new URL(d),N=of(j.pathname,m);j.origin===y.origin&&N!=null?d=N+j.search+j.hash:x=!0}catch{}let b=v3(d,{relative:o}),v=U3(d,{replace:i,state:l,target:c,preventScrollReset:u,relative:o,viewTransition:h});function g(y){r&&r(y),y.defaultPrevented||v(y)}return f.createElement("a",lp({},p,{href:w||b,onClick:x||a?r:g,ref:n,target:c}))});var Av;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(Av||(Av={}));var Pv;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Pv||(Pv={}));function U3(e,t){let{target:n,replace:r,state:o,preventScrollReset:a,relative:i,viewTransition:l}=t===void 0?{}:t,c=Fn(),d=jo(),u=J0(e,{relative:i});return f.useCallback(h=>{if(M3(h,n)){h.preventDefault();let p=r!==void 0?r:Na(d)===Na(u);c(e,{replace:p,state:o,preventScrollReset:a,relative:i,viewTransition:l})}},[d,c,u,r,o,n,e,a,i,l])}function H3(e){let t=f.useRef(cp(e)),n=f.useRef(!1),r=jo(),o=f.useMemo(()=>$3(r.search,n.current?null:t.current),[r.search]),a=Fn(),i=f.useCallback((l,c)=>{const d=cp(typeof l=="function"?l(o):l);n.current=!0,a("?"+d,c)},[a,o]);return[o,i]}const q3=1,V3=1e6;let Ch=0;function G3(){return Ch=(Ch+1)%Number.MAX_SAFE_INTEGER,Ch.toString()}const Sh=new Map,Ov=e=>{if(Sh.has(e))return;const t=setTimeout(()=>{Sh.delete(e),Li({type:"REMOVE_TOAST",toastId:e})},V3);Sh.set(e,t)},Y3=(e,t)=>{switch(t.type){case"ADD_TOAST":return{...e,toasts:[t.toast,...e.toasts].slice(0,q3)};case"UPDATE_TOAST":return{...e,toasts:e.toasts.map(n=>n.id===t.toast.id?{...n,...t.toast}:n)};case"DISMISS_TOAST":{const{toastId:n}=t;return n?Ov(n):e.toasts.forEach(r=>{Ov(r.id)}),{...e,toasts:e.toasts.map(r=>r.id===n||n===void 0?{...r,open:!1}:r)}}case"REMOVE_TOAST":return t.toastId===void 0?{...e,toasts:[]}:{...e,toasts:e.toasts.filter(n=>n.id!==t.toastId)}}},Rc=[];let Mc={toasts:[]};function Li(e){Mc=Y3(Mc,e),Rc.forEach(t=>{t(Mc)})}function Q3({...e}){const t=G3(),n=o=>Li({type:"UPDATE_TOAST",toast:{...o,id:t}}),r=()=>Li({type:"DISMISS_TOAST",toastId:t});return Li({type:"ADD_TOAST",toast:{...e,id:t,open:!0,onOpenChange:o=>{o||r()}}}),{id:t,dismiss:r,update:n}}function K3(){const[e,t]=f.useState(Mc);return f.useEffect(()=>(Rc.push(t),()=>{const n=Rc.indexOf(t);n>-1&&Rc.splice(n,1)}),[e]),{...e,toast:Q3,dismiss:n=>Li({type:"DISMISS_TOAST",toastId:n})}}function le(e,t,{checkForDefaultPrevented:n=!0}={}){return function(o){if(e==null||e(o),n===!1||!o.defaultPrevented)return t==null?void 0:t(o)}}function Dv(e,t){if(typeof e=="function")return e(t);e!=null&&(e.current=t)}function nN(...e){return t=>{let n=!1;const r=e.map(o=>{const a=Dv(o,t);return!n&&typeof a=="function"&&(n=!0),a});if(n)return()=>{for(let o=0;o{const{children:i,...l}=a,c=f.useMemo(()=>l,Object.values(l));return s.jsx(n.Provider,{value:c,children:i})};r.displayName=e+"Provider";function o(a){const i=f.useContext(n);if(i)return i;if(t!==void 0)return t;throw new Error(`\`${a}\` must be used within \`${e}\``)}return[r,o]}function Ra(e,t=[]){let n=[];function r(a,i){const l=f.createContext(i),c=n.length;n=[...n,i];const d=h=>{var v;const{scope:p,children:m,...w}=h,x=((v=p==null?void 0:p[e])==null?void 0:v[c])||l,b=f.useMemo(()=>w,Object.values(w));return s.jsx(x.Provider,{value:b,children:m})};d.displayName=a+"Provider";function u(h,p){var x;const m=((x=p==null?void 0:p[e])==null?void 0:x[c])||l,w=f.useContext(m);if(w)return w;if(i!==void 0)return i;throw new Error(`\`${h}\` must be used within \`${a}\``)}return[d,u]}const o=()=>{const a=n.map(i=>f.createContext(i));return function(l){const c=(l==null?void 0:l[e])||a;return f.useMemo(()=>({[`__scope${e}`]:{...l,[e]:c}}),[l,c])}};return o.scopeName=e,[r,J3(o,...t)]}function J3(...e){const t=e[0];if(e.length===1)return t;const n=()=>{const r=e.map(o=>({useScope:o(),scopeName:o.scopeName}));return function(a){const i=r.reduce((l,{useScope:c,scopeName:d})=>{const h=c(a)[`__scope${d}`];return{...l,...h}},{});return f.useMemo(()=>({[`__scope${t.scopeName}`]:i}),[i])}};return n.scopeName=t.scopeName,n}function ka(e){const t=e4(e),n=f.forwardRef((r,o)=>{const{children:a,...i}=r,l=f.Children.toArray(a),c=l.find(s4);if(c){const d=c.props.children,u=l.map(h=>h===c?f.Children.count(d)>1?f.Children.only(null):f.isValidElement(d)?d.props.children:null:h);return s.jsx(t,{...i,ref:o,children:f.isValidElement(d)?f.cloneElement(d,void 0,u):null})}return s.jsx(t,{...i,ref:o,children:a})});return n.displayName=`${e}.Slot`,n}var Z3=ka("Slot");function e4(e){const t=f.forwardRef((n,r)=>{const{children:o,...a}=n;if(f.isValidElement(o)){const i=r4(o),l=n4(a,o.props);return o.type!==f.Fragment&&(l.ref=r?nN(r,i):i),f.cloneElement(o,l)}return f.Children.count(o)>1?f.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var rN=Symbol("radix.slottable");function t4(e){const t=({children:n})=>s.jsx(s.Fragment,{children:n});return t.displayName=`${e}.Slottable`,t.__radixId=rN,t}function s4(e){return f.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===rN}function n4(e,t){const n={...t};for(const r in t){const o=e[r],a=t[r];/^on[A-Z]/.test(r)?o&&a?n[r]=(...l)=>{const c=a(...l);return o(...l),c}:o&&(n[r]=o):r==="style"?n[r]={...o,...a}:r==="className"&&(n[r]=[o,a].filter(Boolean).join(" "))}return{...e,...n}}function r4(e){var r,o;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(o=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:o.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}function oN(e){const t=e+"CollectionProvider",[n,r]=Ra(t),[o,a]=n(t,{collectionRef:{current:null},itemMap:new Map}),i=x=>{const{scope:b,children:v}=x,g=R.useRef(null),y=R.useRef(new Map).current;return s.jsx(o,{scope:b,itemMap:y,collectionRef:g,children:v})};i.displayName=t;const l=e+"CollectionSlot",c=ka(l),d=R.forwardRef((x,b)=>{const{scope:v,children:g}=x,y=a(l,v),j=We(b,y.collectionRef);return s.jsx(c,{ref:j,children:g})});d.displayName=l;const u=e+"CollectionItemSlot",h="data-radix-collection-item",p=ka(u),m=R.forwardRef((x,b)=>{const{scope:v,children:g,...y}=x,j=R.useRef(null),N=We(b,j),_=a(u,v);return R.useEffect(()=>(_.itemMap.set(j,{ref:j,...y}),()=>void _.itemMap.delete(j))),s.jsx(p,{[h]:"",ref:N,children:g})});m.displayName=u;function w(x){const b=a(e+"CollectionConsumer",x);return R.useCallback(()=>{const g=b.collectionRef.current;if(!g)return[];const y=Array.from(g.querySelectorAll(`[${h}]`));return Array.from(b.itemMap.values()).sort((_,k)=>y.indexOf(_.ref.current)-y.indexOf(k.ref.current))},[b.collectionRef,b.itemMap])}return[{Provider:i,Slot:d,ItemSlot:m},w,r]}var o4=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],be=o4.reduce((e,t)=>{const n=ka(`Primitive.${t}`),r=f.forwardRef((o,a)=>{const{asChild:i,...l}=o,c=i?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),s.jsx(c,{...l,ref:a})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{});function aN(e,t){e&&Oa.flushSync(()=>e.dispatchEvent(t))}function Bs(e){const t=f.useRef(e);return f.useEffect(()=>{t.current=e}),f.useMemo(()=>(...n)=>{var r;return(r=t.current)==null?void 0:r.call(t,...n)},[])}function a4(e,t=globalThis==null?void 0:globalThis.document){const n=Bs(e);f.useEffect(()=>{const r=o=>{o.key==="Escape"&&n(o)};return t.addEventListener("keydown",r,{capture:!0}),()=>t.removeEventListener("keydown",r,{capture:!0})},[n,t])}var i4="DismissableLayer",dp="dismissableLayer.update",l4="dismissableLayer.pointerDownOutside",c4="dismissableLayer.focusOutside",Rv,iN=f.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),Tl=f.forwardRef((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:o,onFocusOutside:a,onInteractOutside:i,onDismiss:l,...c}=e,d=f.useContext(iN),[u,h]=f.useState(null),p=(u==null?void 0:u.ownerDocument)??(globalThis==null?void 0:globalThis.document),[,m]=f.useState({}),w=We(t,k=>h(k)),x=Array.from(d.layers),[b]=[...d.layersWithOutsidePointerEventsDisabled].slice(-1),v=x.indexOf(b),g=u?x.indexOf(u):-1,y=d.layersWithOutsidePointerEventsDisabled.size>0,j=g>=v,N=u4(k=>{const C=k.target,P=[...d.branches].some(A=>A.contains(C));!j||P||(o==null||o(k),i==null||i(k),k.defaultPrevented||l==null||l())},p),_=h4(k=>{const C=k.target;[...d.branches].some(A=>A.contains(C))||(a==null||a(k),i==null||i(k),k.defaultPrevented||l==null||l())},p);return a4(k=>{g===d.layers.size-1&&(r==null||r(k),!k.defaultPrevented&&l&&(k.preventDefault(),l()))},p),f.useEffect(()=>{if(u)return n&&(d.layersWithOutsidePointerEventsDisabled.size===0&&(Rv=p.body.style.pointerEvents,p.body.style.pointerEvents="none"),d.layersWithOutsidePointerEventsDisabled.add(u)),d.layers.add(u),Mv(),()=>{n&&d.layersWithOutsidePointerEventsDisabled.size===1&&(p.body.style.pointerEvents=Rv)}},[u,p,n,d]),f.useEffect(()=>()=>{u&&(d.layers.delete(u),d.layersWithOutsidePointerEventsDisabled.delete(u),Mv())},[u,d]),f.useEffect(()=>{const k=()=>m({});return document.addEventListener(dp,k),()=>document.removeEventListener(dp,k)},[]),s.jsx(be.div,{...c,ref:w,style:{pointerEvents:y?j?"auto":"none":void 0,...e.style},onFocusCapture:le(e.onFocusCapture,_.onFocusCapture),onBlurCapture:le(e.onBlurCapture,_.onBlurCapture),onPointerDownCapture:le(e.onPointerDownCapture,N.onPointerDownCapture)})});Tl.displayName=i4;var d4="DismissableLayerBranch",lN=f.forwardRef((e,t)=>{const n=f.useContext(iN),r=f.useRef(null),o=We(t,r);return f.useEffect(()=>{const a=r.current;if(a)return n.branches.add(a),()=>{n.branches.delete(a)}},[n.branches]),s.jsx(be.div,{...e,ref:o})});lN.displayName=d4;function u4(e,t=globalThis==null?void 0:globalThis.document){const n=Bs(e),r=f.useRef(!1),o=f.useRef(()=>{});return f.useEffect(()=>{const a=l=>{if(l.target&&!r.current){let c=function(){cN(l4,n,d,{discrete:!0})};const d={originalEvent:l};l.pointerType==="touch"?(t.removeEventListener("click",o.current),o.current=c,t.addEventListener("click",o.current,{once:!0})):c()}else t.removeEventListener("click",o.current);r.current=!1},i=window.setTimeout(()=>{t.addEventListener("pointerdown",a)},0);return()=>{window.clearTimeout(i),t.removeEventListener("pointerdown",a),t.removeEventListener("click",o.current)}},[t,n]),{onPointerDownCapture:()=>r.current=!0}}function h4(e,t=globalThis==null?void 0:globalThis.document){const n=Bs(e),r=f.useRef(!1);return f.useEffect(()=>{const o=a=>{a.target&&!r.current&&cN(c4,n,{originalEvent:a},{discrete:!1})};return t.addEventListener("focusin",o),()=>t.removeEventListener("focusin",o)},[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}function Mv(){const e=new CustomEvent(dp);document.dispatchEvent(e)}function cN(e,t,n,{discrete:r}){const o=n.originalEvent.target,a=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?aN(o,a):o.dispatchEvent(a)}var m4=Tl,p4=lN,vt=globalThis!=null&&globalThis.document?f.useLayoutEffect:()=>{},x4="Portal",bu=f.forwardRef((e,t)=>{var l;const{container:n,...r}=e,[o,a]=f.useState(!1);vt(()=>a(!0),[]);const i=n||o&&((l=globalThis==null?void 0:globalThis.document)==null?void 0:l.body);return i?q0.createPortal(s.jsx(be.div,{...r,ref:t}),i):null});bu.displayName=x4;function f4(e,t){return f.useReducer((n,r)=>t[n][r]??n,e)}var Ma=e=>{const{present:t,children:n}=e,r=g4(t),o=typeof n=="function"?n({present:r.isPresent}):f.Children.only(n),a=We(r.ref,v4(o));return typeof n=="function"||r.isPresent?f.cloneElement(o,{ref:a}):null};Ma.displayName="Presence";function g4(e){const[t,n]=f.useState(),r=f.useRef(null),o=f.useRef(e),a=f.useRef("none"),i=e?"mounted":"unmounted",[l,c]=f4(i,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return f.useEffect(()=>{const d=sc(r.current);a.current=l==="mounted"?d:"none"},[l]),vt(()=>{const d=r.current,u=o.current;if(u!==e){const p=a.current,m=sc(d);e?c("MOUNT"):m==="none"||(d==null?void 0:d.display)==="none"?c("UNMOUNT"):c(u&&p!==m?"ANIMATION_OUT":"UNMOUNT"),o.current=e}},[e,c]),vt(()=>{if(t){let d;const u=t.ownerDocument.defaultView??window,h=m=>{const x=sc(r.current).includes(m.animationName);if(m.target===t&&x&&(c("ANIMATION_END"),!o.current)){const b=t.style.animationFillMode;t.style.animationFillMode="forwards",d=u.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=b)})}},p=m=>{m.target===t&&(a.current=sc(r.current))};return t.addEventListener("animationstart",p),t.addEventListener("animationcancel",h),t.addEventListener("animationend",h),()=>{u.clearTimeout(d),t.removeEventListener("animationstart",p),t.removeEventListener("animationcancel",h),t.removeEventListener("animationend",h)}}else c("ANIMATION_END")},[t,c]),{isPresent:["mounted","unmountSuspended"].includes(l),ref:f.useCallback(d=>{r.current=d?getComputedStyle(d):null,n(d)},[])}}function sc(e){return(e==null?void 0:e.animationName)||"none"}function v4(e){var r,o;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(o=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:o.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var y4=px[" useInsertionEffect ".trim().toString()]||vt;function kd({prop:e,defaultProp:t,onChange:n=()=>{},caller:r}){const[o,a,i]=b4({defaultProp:t,onChange:n}),l=e!==void 0,c=l?e:o;{const u=f.useRef(e!==void 0);f.useEffect(()=>{const h=u.current;h!==l&&console.warn(`${r} is changing from ${h?"controlled":"uncontrolled"} to ${l?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),u.current=l},[l,r])}const d=f.useCallback(u=>{var h;if(l){const p=w4(u)?u(e):u;p!==e&&((h=i.current)==null||h.call(i,p))}else a(u)},[l,e,a,i]);return[c,d]}function b4({defaultProp:e,onChange:t}){const[n,r]=f.useState(e),o=f.useRef(n),a=f.useRef(t);return y4(()=>{a.current=t},[t]),f.useEffect(()=>{var i;o.current!==n&&((i=a.current)==null||i.call(a,n),o.current=n)},[n,o]),[n,r,a]}function w4(e){return typeof e=="function"}var dN=Object.freeze({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"}),j4="VisuallyHidden",wu=f.forwardRef((e,t)=>s.jsx(be.span,{...e,ref:t,style:{...dN,...e.style}}));wu.displayName=j4;var N4=wu,df="ToastProvider",[uf,k4,_4]=oN("Toast"),[uN,n$]=Ra("Toast",[_4]),[C4,ju]=uN(df),hN=e=>{const{__scopeToast:t,label:n="Notification",duration:r=5e3,swipeDirection:o="right",swipeThreshold:a=50,children:i}=e,[l,c]=f.useState(null),[d,u]=f.useState(0),h=f.useRef(!1),p=f.useRef(!1);return n.trim()||console.error(`Invalid prop \`label\` supplied to \`${df}\`. Expected non-empty \`string\`.`),s.jsx(uf.Provider,{scope:t,children:s.jsx(C4,{scope:t,label:n,duration:r,swipeDirection:o,swipeThreshold:a,toastCount:d,viewport:l,onViewportChange:c,onToastAdd:f.useCallback(()=>u(m=>m+1),[]),onToastRemove:f.useCallback(()=>u(m=>m-1),[]),isFocusedToastEscapeKeyDownRef:h,isClosePausedRef:p,children:i})})};hN.displayName=df;var mN="ToastViewport",S4=["F8"],up="toast.viewportPause",hp="toast.viewportResume",pN=f.forwardRef((e,t)=>{const{__scopeToast:n,hotkey:r=S4,label:o="Notifications ({hotkey})",...a}=e,i=ju(mN,n),l=k4(n),c=f.useRef(null),d=f.useRef(null),u=f.useRef(null),h=f.useRef(null),p=We(t,h,i.onViewportChange),m=r.join("+").replace(/Key/g,"").replace(/Digit/g,""),w=i.toastCount>0;f.useEffect(()=>{const b=v=>{var y;r.length!==0&&r.every(j=>v[j]||v.code===j)&&((y=h.current)==null||y.focus())};return document.addEventListener("keydown",b),()=>document.removeEventListener("keydown",b)},[r]),f.useEffect(()=>{const b=c.current,v=h.current;if(w&&b&&v){const g=()=>{if(!i.isClosePausedRef.current){const _=new CustomEvent(up);v.dispatchEvent(_),i.isClosePausedRef.current=!0}},y=()=>{if(i.isClosePausedRef.current){const _=new CustomEvent(hp);v.dispatchEvent(_),i.isClosePausedRef.current=!1}},j=_=>{!b.contains(_.relatedTarget)&&y()},N=()=>{b.contains(document.activeElement)||y()};return b.addEventListener("focusin",g),b.addEventListener("focusout",j),b.addEventListener("pointermove",g),b.addEventListener("pointerleave",N),window.addEventListener("blur",g),window.addEventListener("focus",y),()=>{b.removeEventListener("focusin",g),b.removeEventListener("focusout",j),b.removeEventListener("pointermove",g),b.removeEventListener("pointerleave",N),window.removeEventListener("blur",g),window.removeEventListener("focus",y)}}},[w,i.isClosePausedRef]);const x=f.useCallback(({tabbingDirection:b})=>{const g=l().map(y=>{const j=y.ref.current,N=[j,...z4(j)];return b==="forwards"?N:N.reverse()});return(b==="forwards"?g.reverse():g).flat()},[l]);return f.useEffect(()=>{const b=h.current;if(b){const v=g=>{var N,_,k;const y=g.altKey||g.ctrlKey||g.metaKey;if(g.key==="Tab"&&!y){const C=document.activeElement,P=g.shiftKey;if(g.target===b&&P){(N=d.current)==null||N.focus();return}const M=x({tabbingDirection:P?"backwards":"forwards"}),K=M.findIndex(O=>O===C);Eh(M.slice(K+1))?g.preventDefault():P?(_=d.current)==null||_.focus():(k=u.current)==null||k.focus()}};return b.addEventListener("keydown",v),()=>b.removeEventListener("keydown",v)}},[l,x]),s.jsxs(p4,{ref:c,role:"region","aria-label":o.replace("{hotkey}",m),tabIndex:-1,style:{pointerEvents:w?void 0:"none"},children:[w&&s.jsx(mp,{ref:d,onFocusFromOutsideViewport:()=>{const b=x({tabbingDirection:"forwards"});Eh(b)}}),s.jsx(uf.Slot,{scope:n,children:s.jsx(be.ol,{tabIndex:-1,...a,ref:p})}),w&&s.jsx(mp,{ref:u,onFocusFromOutsideViewport:()=>{const b=x({tabbingDirection:"backwards"});Eh(b)}})]})});pN.displayName=mN;var xN="ToastFocusProxy",mp=f.forwardRef((e,t)=>{const{__scopeToast:n,onFocusFromOutsideViewport:r,...o}=e,a=ju(xN,n);return s.jsx(wu,{"aria-hidden":!0,tabIndex:0,...o,ref:t,style:{position:"fixed"},onFocus:i=>{var d;const l=i.relatedTarget;!((d=a.viewport)!=null&&d.contains(l))&&r()}})});mp.displayName=xN;var Il="Toast",E4="toast.swipeStart",T4="toast.swipeMove",I4="toast.swipeCancel",A4="toast.swipeEnd",fN=f.forwardRef((e,t)=>{const{forceMount:n,open:r,defaultOpen:o,onOpenChange:a,...i}=e,[l,c]=kd({prop:r,defaultProp:o??!0,onChange:a,caller:Il});return s.jsx(Ma,{present:n||l,children:s.jsx(D4,{open:l,...i,ref:t,onClose:()=>c(!1),onPause:Bs(e.onPause),onResume:Bs(e.onResume),onSwipeStart:le(e.onSwipeStart,d=>{d.currentTarget.setAttribute("data-swipe","start")}),onSwipeMove:le(e.onSwipeMove,d=>{const{x:u,y:h}=d.detail.delta;d.currentTarget.setAttribute("data-swipe","move"),d.currentTarget.style.setProperty("--radix-toast-swipe-move-x",`${u}px`),d.currentTarget.style.setProperty("--radix-toast-swipe-move-y",`${h}px`)}),onSwipeCancel:le(e.onSwipeCancel,d=>{d.currentTarget.setAttribute("data-swipe","cancel"),d.currentTarget.style.removeProperty("--radix-toast-swipe-move-x"),d.currentTarget.style.removeProperty("--radix-toast-swipe-move-y"),d.currentTarget.style.removeProperty("--radix-toast-swipe-end-x"),d.currentTarget.style.removeProperty("--radix-toast-swipe-end-y")}),onSwipeEnd:le(e.onSwipeEnd,d=>{const{x:u,y:h}=d.detail.delta;d.currentTarget.setAttribute("data-swipe","end"),d.currentTarget.style.removeProperty("--radix-toast-swipe-move-x"),d.currentTarget.style.removeProperty("--radix-toast-swipe-move-y"),d.currentTarget.style.setProperty("--radix-toast-swipe-end-x",`${u}px`),d.currentTarget.style.setProperty("--radix-toast-swipe-end-y",`${h}px`),c(!1)})})})});fN.displayName=Il;var[P4,O4]=uN(Il,{onClose(){}}),D4=f.forwardRef((e,t)=>{const{__scopeToast:n,type:r="foreground",duration:o,open:a,onClose:i,onEscapeKeyDown:l,onPause:c,onResume:d,onSwipeStart:u,onSwipeMove:h,onSwipeCancel:p,onSwipeEnd:m,...w}=e,x=ju(Il,n),[b,v]=f.useState(null),g=We(t,O=>v(O)),y=f.useRef(null),j=f.useRef(null),N=o||x.duration,_=f.useRef(0),k=f.useRef(N),C=f.useRef(0),{onToastAdd:P,onToastRemove:A}=x,H=Bs(()=>{var X;(b==null?void 0:b.contains(document.activeElement))&&((X=x.viewport)==null||X.focus()),i()}),M=f.useCallback(O=>{!O||O===1/0||(window.clearTimeout(C.current),_.current=new Date().getTime(),C.current=window.setTimeout(H,O))},[H]);f.useEffect(()=>{const O=x.viewport;if(O){const X=()=>{M(k.current),d==null||d()},q=()=>{const Y=new Date().getTime()-_.current;k.current=k.current-Y,window.clearTimeout(C.current),c==null||c()};return O.addEventListener(up,q),O.addEventListener(hp,X),()=>{O.removeEventListener(up,q),O.removeEventListener(hp,X)}}},[x.viewport,N,c,d,M]),f.useEffect(()=>{a&&!x.isClosePausedRef.current&&M(N)},[a,N,x.isClosePausedRef,M]),f.useEffect(()=>(P(),()=>A()),[P,A]);const K=f.useMemo(()=>b?NN(b):null,[b]);return x.viewport?s.jsxs(s.Fragment,{children:[K&&s.jsx(R4,{__scopeToast:n,role:"status","aria-live":r==="foreground"?"assertive":"polite","aria-atomic":!0,children:K}),s.jsx(P4,{scope:n,onClose:H,children:Oa.createPortal(s.jsx(uf.ItemSlot,{scope:n,children:s.jsx(m4,{asChild:!0,onEscapeKeyDown:le(l,()=>{x.isFocusedToastEscapeKeyDownRef.current||H(),x.isFocusedToastEscapeKeyDownRef.current=!1}),children:s.jsx(be.li,{role:"status","aria-live":"off","aria-atomic":!0,tabIndex:0,"data-state":a?"open":"closed","data-swipe-direction":x.swipeDirection,...w,ref:g,style:{userSelect:"none",touchAction:"none",...e.style},onKeyDown:le(e.onKeyDown,O=>{O.key==="Escape"&&(l==null||l(O.nativeEvent),O.nativeEvent.defaultPrevented||(x.isFocusedToastEscapeKeyDownRef.current=!0,H()))}),onPointerDown:le(e.onPointerDown,O=>{O.button===0&&(y.current={x:O.clientX,y:O.clientY})}),onPointerMove:le(e.onPointerMove,O=>{if(!y.current)return;const X=O.clientX-y.current.x,q=O.clientY-y.current.y,Y=!!j.current,E=["left","right"].includes(x.swipeDirection),T=["left","up"].includes(x.swipeDirection)?Math.min:Math.max,$=E?T(0,X):0,Z=E?0:T(0,q),W=O.pointerType==="touch"?10:2,ee={x:$,y:Z},J={originalEvent:O,delta:ee};Y?(j.current=ee,nc(T4,h,J,{discrete:!1})):$v(ee,x.swipeDirection,W)?(j.current=ee,nc(E4,u,J,{discrete:!1}),O.target.setPointerCapture(O.pointerId)):(Math.abs(X)>W||Math.abs(q)>W)&&(y.current=null)}),onPointerUp:le(e.onPointerUp,O=>{const X=j.current,q=O.target;if(q.hasPointerCapture(O.pointerId)&&q.releasePointerCapture(O.pointerId),j.current=null,y.current=null,X){const Y=O.currentTarget,E={originalEvent:O,delta:X};$v(X,x.swipeDirection,x.swipeThreshold)?nc(A4,m,E,{discrete:!0}):nc(I4,p,E,{discrete:!0}),Y.addEventListener("click",T=>T.preventDefault(),{once:!0})}})})})}),x.viewport)})]}):null}),R4=e=>{const{__scopeToast:t,children:n,...r}=e,o=ju(Il,t),[a,i]=f.useState(!1),[l,c]=f.useState(!1);return L4(()=>i(!0)),f.useEffect(()=>{const d=window.setTimeout(()=>c(!0),1e3);return()=>window.clearTimeout(d)},[]),l?null:s.jsx(bu,{asChild:!0,children:s.jsx(wu,{...r,children:a&&s.jsxs(s.Fragment,{children:[o.label," ",n]})})})},M4="ToastTitle",gN=f.forwardRef((e,t)=>{const{__scopeToast:n,...r}=e;return s.jsx(be.div,{...r,ref:t})});gN.displayName=M4;var $4="ToastDescription",vN=f.forwardRef((e,t)=>{const{__scopeToast:n,...r}=e;return s.jsx(be.div,{...r,ref:t})});vN.displayName=$4;var yN="ToastAction",bN=f.forwardRef((e,t)=>{const{altText:n,...r}=e;return n.trim()?s.jsx(jN,{altText:n,asChild:!0,children:s.jsx(hf,{...r,ref:t})}):(console.error(`Invalid prop \`altText\` supplied to \`${yN}\`. Expected non-empty \`string\`.`),null)});bN.displayName=yN;var wN="ToastClose",hf=f.forwardRef((e,t)=>{const{__scopeToast:n,...r}=e,o=O4(wN,n);return s.jsx(jN,{asChild:!0,children:s.jsx(be.button,{type:"button",...r,ref:t,onClick:le(e.onClick,o.onClose)})})});hf.displayName=wN;var jN=f.forwardRef((e,t)=>{const{__scopeToast:n,altText:r,...o}=e;return s.jsx(be.div,{"data-radix-toast-announce-exclude":"","data-radix-toast-announce-alt":r||void 0,...o,ref:t})});function NN(e){const t=[];return Array.from(e.childNodes).forEach(r=>{if(r.nodeType===r.TEXT_NODE&&r.textContent&&t.push(r.textContent),F4(r)){const o=r.ariaHidden||r.hidden||r.style.display==="none",a=r.dataset.radixToastAnnounceExclude==="";if(!o)if(a){const i=r.dataset.radixToastAnnounceAlt;i&&t.push(i)}else t.push(...NN(r))}}),t}function nc(e,t,n,{discrete:r}){const o=n.originalEvent.currentTarget,a=new CustomEvent(e,{bubbles:!0,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?aN(o,a):o.dispatchEvent(a)}var $v=(e,t,n=0)=>{const r=Math.abs(e.x),o=Math.abs(e.y),a=r>o;return t==="left"||t==="right"?a&&r>n:!a&&o>n};function L4(e=()=>{}){const t=Bs(e);vt(()=>{let n=0,r=0;return n=window.requestAnimationFrame(()=>r=window.requestAnimationFrame(t)),()=>{window.cancelAnimationFrame(n),window.cancelAnimationFrame(r)}},[t])}function F4(e){return e.nodeType===e.ELEMENT_NODE}function z4(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function Eh(e){const t=document.activeElement;return e.some(n=>n===t?!0:(n.focus(),document.activeElement!==t))}var B4=hN,kN=pN,_N=fN,CN=gN,SN=vN,EN=bN,TN=hf;function IN(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var o=e.length;for(t=0;ttypeof e=="boolean"?`${e}`:e===0?"0":e,Fv=AN,mf=(e,t)=>n=>{var r;if((t==null?void 0:t.variants)==null)return Fv(e,n==null?void 0:n.class,n==null?void 0:n.className);const{variants:o,defaultVariants:a}=t,i=Object.keys(o).map(d=>{const u=n==null?void 0:n[d],h=a==null?void 0:a[d];if(u===null)return null;const p=Lv(u)||Lv(h);return o[d][p]}),l=n&&Object.entries(n).reduce((d,u)=>{let[h,p]=u;return p===void 0||(d[h]=p),d},{}),c=t==null||(r=t.compoundVariants)===null||r===void 0?void 0:r.reduce((d,u)=>{let{class:h,className:p,...m}=u;return Object.entries(m).every(w=>{let[x,b]=w;return Array.isArray(b)?b.includes({...a,...l}[x]):{...a,...l}[x]===b})?[...d,h,p]:d},[]);return Fv(e,i,c,n==null?void 0:n.class,n==null?void 0:n.className)};/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const W4=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),PN=(...e)=>e.filter((t,n,r)=>!!t&&t.trim()!==""&&r.indexOf(t)===n).join(" ").trim();/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */var U4={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const H4=f.forwardRef(({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:o="",children:a,iconNode:i,...l},c)=>f.createElement("svg",{ref:c,...U4,width:t,height:t,stroke:e,strokeWidth:r?Number(n)*24/Number(t):n,className:PN("lucide",o),...l},[...i.map(([d,u])=>f.createElement(d,u)),...Array.isArray(a)?a:[a]]));/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const _e=(e,t)=>{const n=f.forwardRef(({className:r,...o},a)=>f.createElement(H4,{ref:a,iconNode:t,className:PN(`lucide-${W4(e)}`,r),...o}));return n.displayName=`${e}`,n};/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const q4=_e("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const zv=_e("ArrowRight",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Bv=_e("Bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const ve=_e("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Al=_e("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const V4=_e("ChevronUp",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const G4=_e("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const ON=_e("CircleCheckBig",[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Y4=_e("CloudDownload",[["path",{d:"M12 13v8l-4-4",key:"1f5nwf"}],["path",{d:"m12 21 4-4",key:"1lfcce"}],["path",{d:"M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284",key:"ui1hmy"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Q4=_e("Drama",[["path",{d:"M10 11h.01",key:"d2at3l"}],["path",{d:"M14 6h.01",key:"k028ub"}],["path",{d:"M18 6h.01",key:"1v4wsw"}],["path",{d:"M6.5 13.1h.01",key:"1748ia"}],["path",{d:"M22 5c0 9-4 12-6 12s-6-3-6-12c0-2 2-3 6-3s6 1 6 3",key:"172yzv"}],["path",{d:"M17.4 9.9c-.8.8-2 .8-2.8 0",key:"1obv0w"}],["path",{d:"M10.1 7.1C9 7.2 7.7 7.7 6 8.6c-3.5 2-4.7 3.9-3.7 5.6 4.5 7.8 9.5 8.4 11.2 7.4.9-.5 1.9-2.1 1.9-4.7",key:"rqjl8i"}],["path",{d:"M9.1 16.5c.3-1.1 1.4-1.7 2.4-1.4",key:"1mr6wy"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const K4=_e("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const ta=_e("FileCode",[["path",{d:"M10 12.5 8 15l2 2.5",key:"1tg20x"}],["path",{d:"m14 12.5 2 2.5-2 2.5",key:"yinavb"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z",key:"1mlx9k"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const X4=_e("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const J4=_e("GitPullRequest",[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M13 6h3a2 2 0 0 1 2 2v7",key:"1yeb86"}],["line",{x1:"6",x2:"6",y1:"9",y2:"21",key:"rroup"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Z4=_e("Globe",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Wv=_e("Link",[["path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",key:"1cjeqo"}],["path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",key:"19qd67"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const e6=_e("Lock",[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const t6=_e("Menu",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Uv=_e("MonitorPlay",[["path",{d:"M10 7.75a.75.75 0 0 1 1.142-.638l3.664 2.249a.75.75 0 0 1 0 1.278l-3.664 2.25a.75.75 0 0 1-1.142-.64z",key:"1pctta"}],["path",{d:"M12 17v4",key:"1riwvh"}],["path",{d:"M8 21h8",key:"1ev6f3"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2",key:"x3v2xh"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Hv=_e("MousePointerClick",[["path",{d:"M14 4.1 12 6",key:"ita8i4"}],["path",{d:"m5.1 8-2.9-.8",key:"1go3kf"}],["path",{d:"m6 12-1.9 2",key:"mnht97"}],["path",{d:"M7.2 2.2 8 5.1",key:"1cfko1"}],["path",{d:"M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z",key:"s0h3yz"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const s6=_e("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const n6=_e("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const qv=_e("Server",[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Vv=_e("Shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const r6=_e("Sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const o6=_e("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const a6=_e("UserCheck",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["polyline",{points:"16 11 18 13 22 9",key:"1pwet4"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const i6=_e("Users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["path",{d:"M16 3.13a4 4 0 0 1 0 7.75",key:"1da9ce"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const l6=_e("Wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);/** * @license lucide-react v0.462.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const $a=_e("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]),pf="-",c6=e=>{const t=u6(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:i=>{const l=i.split(pf);return l[0]===""&&l.length!==1&&l.shift(),DN(l,t)||d6(i)},getConflictingClassGroupIds:(i,l)=>{const c=n[i]||[];return l&&r[i]?[...c,...r[i]]:c}}},DN=(e,t)=>{var i;if(e.length===0)return t.classGroupId;const n=e[0],r=t.nextPart.get(n),o=r?DN(e.slice(1),r):void 0;if(o)return o;if(t.validators.length===0)return;const a=e.join(pf);return(i=t.validators.find(({validator:l})=>l(a)))==null?void 0:i.classGroupId},Gv=/^\[(.+)\]$/,d6=e=>{if(Gv.test(e)){const t=Gv.exec(e)[1],n=t==null?void 0:t.substring(0,t.indexOf(":"));if(n)return"arbitrary.."+n}},u6=e=>{const{theme:t,prefix:n}=e,r={nextPart:new Map,validators:[]};return m6(Object.entries(e.classGroups),n).forEach(([a,i])=>{pp(i,r,a,t)}),r},pp=(e,t,n,r)=>{e.forEach(o=>{if(typeof o=="string"){const a=o===""?t:Yv(t,o);a.classGroupId=n;return}if(typeof o=="function"){if(h6(o)){pp(o(r),t,n,r);return}t.validators.push({validator:o,classGroupId:n});return}Object.entries(o).forEach(([a,i])=>{pp(i,Yv(t,a),n,r)})})},Yv=(e,t)=>{let n=e;return t.split(pf).forEach(r=>{n.nextPart.has(r)||n.nextPart.set(r,{nextPart:new Map,validators:[]}),n=n.nextPart.get(r)}),n},h6=e=>e.isThemeGetter,m6=(e,t)=>t?e.map(([n,r])=>{const o=r.map(a=>typeof a=="string"?t+a:typeof a=="object"?Object.fromEntries(Object.entries(a).map(([i,l])=>[t+i,l])):a);return[n,o]}):e,p6=e=>{if(e<1)return{get:()=>{},set:()=>{}};let t=0,n=new Map,r=new Map;const o=(a,i)=>{n.set(a,i),t++,t>e&&(t=0,r=n,n=new Map)};return{get(a){let i=n.get(a);if(i!==void 0)return i;if((i=r.get(a))!==void 0)return o(a,i),i},set(a,i){n.has(a)?n.set(a,i):o(a,i)}}},RN="!",x6=e=>{const{separator:t,experimentalParseClassName:n}=e,r=t.length===1,o=t[0],a=t.length,i=l=>{const c=[];let d=0,u=0,h;for(let b=0;bu?h-u:void 0;return{modifiers:c,hasImportantModifier:m,baseClassName:w,maybePostfixModifierPosition:x}};return n?l=>n({className:l,parseClassName:i}):i},f6=e=>{if(e.length<=1)return e;const t=[];let n=[];return e.forEach(r=>{r[0]==="["?(t.push(...n.sort(),r),n=[]):n.push(r)}),t.push(...n.sort()),t},g6=e=>({cache:p6(e.cacheSize),parseClassName:x6(e),...c6(e)}),v6=/\s+/,y6=(e,t)=>{const{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:o}=t,a=[],i=e.trim().split(v6);let l="";for(let c=i.length-1;c>=0;c-=1){const d=i[c],{modifiers:u,hasImportantModifier:h,baseClassName:p,maybePostfixModifierPosition:m}=n(d);let w=!!m,x=r(w?p.substring(0,m):p);if(!x){if(!w){l=d+(l.length>0?" "+l:l);continue}if(x=r(p),!x){l=d+(l.length>0?" "+l:l);continue}w=!1}const b=f6(u).join(":"),v=h?b+RN:b,g=v+x;if(a.includes(g))continue;a.push(g);const y=o(x,w);for(let j=0;j0?" "+l:l)}return l};function b6(){let e=0,t,n,r="";for(;e{if(typeof e=="string")return e;let t,n="";for(let r=0;rh(u),e());return n=g6(d),r=n.cache.get,o=n.cache.set,a=l,l(c)}function l(c){const d=r(c);if(d)return d;const u=y6(c,n);return o(c,u),u}return function(){return a(b6.apply(null,arguments))}}const $e=e=>{const t=n=>n[e]||[];return t.isThemeGetter=!0,t},$N=/^\[(?:([a-z-]+):)?(.+)\]$/i,j6=/^\d+\/\d+$/,N6=new Set(["px","full","screen"]),k6=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,_6=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,C6=/^(rgba?|hsla?|hwb|(ok)?(lab|lch))\(.+\)$/,S6=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,E6=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,mn=e=>sa(e)||N6.has(e)||j6.test(e),qn=e=>La(e,"length",M6),sa=e=>!!e&&!Number.isNaN(Number(e)),Th=e=>La(e,"number",sa),si=e=>!!e&&Number.isInteger(Number(e)),T6=e=>e.endsWith("%")&&sa(e.slice(0,-1)),ie=e=>$N.test(e),Vn=e=>k6.test(e),I6=new Set(["length","size","percentage"]),A6=e=>La(e,I6,LN),P6=e=>La(e,"position",LN),O6=new Set(["image","url"]),D6=e=>La(e,O6,L6),R6=e=>La(e,"",$6),ni=()=>!0,La=(e,t,n)=>{const r=$N.exec(e);return r?r[1]?typeof t=="string"?r[1]===t:t.has(r[1]):n(r[2]):!1},M6=e=>_6.test(e)&&!C6.test(e),LN=()=>!1,$6=e=>S6.test(e),L6=e=>E6.test(e),F6=()=>{const e=$e("colors"),t=$e("spacing"),n=$e("blur"),r=$e("brightness"),o=$e("borderColor"),a=$e("borderRadius"),i=$e("borderSpacing"),l=$e("borderWidth"),c=$e("contrast"),d=$e("grayscale"),u=$e("hueRotate"),h=$e("invert"),p=$e("gap"),m=$e("gradientColorStops"),w=$e("gradientColorStopPositions"),x=$e("inset"),b=$e("margin"),v=$e("opacity"),g=$e("padding"),y=$e("saturate"),j=$e("scale"),N=$e("sepia"),_=$e("skew"),k=$e("space"),C=$e("translate"),P=()=>["auto","contain","none"],A=()=>["auto","hidden","clip","visible","scroll"],H=()=>["auto",ie,t],M=()=>[ie,t],K=()=>["",mn,qn],O=()=>["auto",sa,ie],X=()=>["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top"],q=()=>["solid","dashed","dotted","double","none"],Y=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],E=()=>["start","end","center","between","around","evenly","stretch"],T=()=>["","0",ie],$=()=>["auto","avoid","all","avoid-page","page","left","right","column"],Z=()=>[sa,ie];return{cacheSize:500,separator:":",theme:{colors:[ni],spacing:[mn,qn],blur:["none","",Vn,ie],brightness:Z(),borderColor:[e],borderRadius:["none","","full",Vn,ie],borderSpacing:M(),borderWidth:K(),contrast:Z(),grayscale:T(),hueRotate:Z(),invert:T(),gap:M(),gradientColorStops:[e],gradientColorStopPositions:[T6,qn],inset:H(),margin:H(),opacity:Z(),padding:M(),saturate:Z(),scale:Z(),sepia:T(),skew:Z(),space:M(),translate:M()},classGroups:{aspect:[{aspect:["auto","square","video",ie]}],container:["container"],columns:[{columns:[Vn]}],"break-after":[{"break-after":$()}],"break-before":[{"break-before":$()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:[...X(),ie]}],overflow:[{overflow:A()}],"overflow-x":[{"overflow-x":A()}],"overflow-y":[{"overflow-y":A()}],overscroll:[{overscroll:P()}],"overscroll-x":[{"overscroll-x":P()}],"overscroll-y":[{"overscroll-y":P()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:[x]}],"inset-x":[{"inset-x":[x]}],"inset-y":[{"inset-y":[x]}],start:[{start:[x]}],end:[{end:[x]}],top:[{top:[x]}],right:[{right:[x]}],bottom:[{bottom:[x]}],left:[{left:[x]}],visibility:["visible","invisible","collapse"],z:[{z:["auto",si,ie]}],basis:[{basis:H()}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",ie]}],grow:[{grow:T()}],shrink:[{shrink:T()}],order:[{order:["first","last","none",si,ie]}],"grid-cols":[{"grid-cols":[ni]}],"col-start-end":[{col:["auto",{span:["full",si,ie]},ie]}],"col-start":[{"col-start":O()}],"col-end":[{"col-end":O()}],"grid-rows":[{"grid-rows":[ni]}],"row-start-end":[{row:["auto",{span:[si,ie]},ie]}],"row-start":[{"row-start":O()}],"row-end":[{"row-end":O()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr",ie]}],"auto-rows":[{"auto-rows":["auto","min","max","fr",ie]}],gap:[{gap:[p]}],"gap-x":[{"gap-x":[p]}],"gap-y":[{"gap-y":[p]}],"justify-content":[{justify:["normal",...E()]}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:["normal",...E(),"baseline"]}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":[...E(),"baseline"]}],"place-items":[{"place-items":["start","end","center","baseline","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:[g]}],px:[{px:[g]}],py:[{py:[g]}],ps:[{ps:[g]}],pe:[{pe:[g]}],pt:[{pt:[g]}],pr:[{pr:[g]}],pb:[{pb:[g]}],pl:[{pl:[g]}],m:[{m:[b]}],mx:[{mx:[b]}],my:[{my:[b]}],ms:[{ms:[b]}],me:[{me:[b]}],mt:[{mt:[b]}],mr:[{mr:[b]}],mb:[{mb:[b]}],ml:[{ml:[b]}],"space-x":[{"space-x":[k]}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":[k]}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max","fit","svw","lvw","dvw",ie,t]}],"min-w":[{"min-w":[ie,t,"min","max","fit"]}],"max-w":[{"max-w":[ie,t,"none","full","min","max","fit","prose",{screen:[Vn]},Vn]}],h:[{h:[ie,t,"auto","min","max","fit","svh","lvh","dvh"]}],"min-h":[{"min-h":[ie,t,"min","max","fit","svh","lvh","dvh"]}],"max-h":[{"max-h":[ie,t,"min","max","fit","svh","lvh","dvh"]}],size:[{size:[ie,t,"auto","min","max","fit"]}],"font-size":[{text:["base",Vn,qn]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black",Th]}],"font-family":[{font:[ni]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",ie]}],"line-clamp":[{"line-clamp":["none",sa,Th]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",mn,ie]}],"list-image":[{"list-image":["none",ie]}],"list-style-type":[{list:["none","disc","decimal",ie]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:[e]}],"placeholder-opacity":[{"placeholder-opacity":[v]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"text-color":[{text:[e]}],"text-opacity":[{"text-opacity":[v]}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...q(),"wavy"]}],"text-decoration-thickness":[{decoration:["auto","from-font",mn,qn]}],"underline-offset":[{"underline-offset":["auto",mn,ie]}],"text-decoration-color":[{decoration:[e]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:M()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ie]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ie]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":[v]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:[...X(),P6]}],"bg-repeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain",A6]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]},D6]}],"bg-color":[{bg:[e]}],"gradient-from-pos":[{from:[w]}],"gradient-via-pos":[{via:[w]}],"gradient-to-pos":[{to:[w]}],"gradient-from":[{from:[m]}],"gradient-via":[{via:[m]}],"gradient-to":[{to:[m]}],rounded:[{rounded:[a]}],"rounded-s":[{"rounded-s":[a]}],"rounded-e":[{"rounded-e":[a]}],"rounded-t":[{"rounded-t":[a]}],"rounded-r":[{"rounded-r":[a]}],"rounded-b":[{"rounded-b":[a]}],"rounded-l":[{"rounded-l":[a]}],"rounded-ss":[{"rounded-ss":[a]}],"rounded-se":[{"rounded-se":[a]}],"rounded-ee":[{"rounded-ee":[a]}],"rounded-es":[{"rounded-es":[a]}],"rounded-tl":[{"rounded-tl":[a]}],"rounded-tr":[{"rounded-tr":[a]}],"rounded-br":[{"rounded-br":[a]}],"rounded-bl":[{"rounded-bl":[a]}],"border-w":[{border:[l]}],"border-w-x":[{"border-x":[l]}],"border-w-y":[{"border-y":[l]}],"border-w-s":[{"border-s":[l]}],"border-w-e":[{"border-e":[l]}],"border-w-t":[{"border-t":[l]}],"border-w-r":[{"border-r":[l]}],"border-w-b":[{"border-b":[l]}],"border-w-l":[{"border-l":[l]}],"border-opacity":[{"border-opacity":[v]}],"border-style":[{border:[...q(),"hidden"]}],"divide-x":[{"divide-x":[l]}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":[l]}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":[v]}],"divide-style":[{divide:q()}],"border-color":[{border:[o]}],"border-color-x":[{"border-x":[o]}],"border-color-y":[{"border-y":[o]}],"border-color-s":[{"border-s":[o]}],"border-color-e":[{"border-e":[o]}],"border-color-t":[{"border-t":[o]}],"border-color-r":[{"border-r":[o]}],"border-color-b":[{"border-b":[o]}],"border-color-l":[{"border-l":[o]}],"divide-color":[{divide:[o]}],"outline-style":[{outline:["",...q()]}],"outline-offset":[{"outline-offset":[mn,ie]}],"outline-w":[{outline:[mn,qn]}],"outline-color":[{outline:[e]}],"ring-w":[{ring:K()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:[e]}],"ring-opacity":[{"ring-opacity":[v]}],"ring-offset-w":[{"ring-offset":[mn,qn]}],"ring-offset-color":[{"ring-offset":[e]}],shadow:[{shadow:["","inner","none",Vn,R6]}],"shadow-color":[{shadow:[ni]}],opacity:[{opacity:[v]}],"mix-blend":[{"mix-blend":[...Y(),"plus-lighter","plus-darker"]}],"bg-blend":[{"bg-blend":Y()}],filter:[{filter:["","none"]}],blur:[{blur:[n]}],brightness:[{brightness:[r]}],contrast:[{contrast:[c]}],"drop-shadow":[{"drop-shadow":["","none",Vn,ie]}],grayscale:[{grayscale:[d]}],"hue-rotate":[{"hue-rotate":[u]}],invert:[{invert:[h]}],saturate:[{saturate:[y]}],sepia:[{sepia:[N]}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":[n]}],"backdrop-brightness":[{"backdrop-brightness":[r]}],"backdrop-contrast":[{"backdrop-contrast":[c]}],"backdrop-grayscale":[{"backdrop-grayscale":[d]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[u]}],"backdrop-invert":[{"backdrop-invert":[h]}],"backdrop-opacity":[{"backdrop-opacity":[v]}],"backdrop-saturate":[{"backdrop-saturate":[y]}],"backdrop-sepia":[{"backdrop-sepia":[N]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":[i]}],"border-spacing-x":[{"border-spacing-x":[i]}],"border-spacing-y":[{"border-spacing-y":[i]}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform",ie]}],duration:[{duration:Z()}],ease:[{ease:["linear","in","out","in-out",ie]}],delay:[{delay:Z()}],animate:[{animate:["none","spin","ping","pulse","bounce",ie]}],transform:[{transform:["","gpu","none"]}],scale:[{scale:[j]}],"scale-x":[{"scale-x":[j]}],"scale-y":[{"scale-y":[j]}],rotate:[{rotate:[si,ie]}],"translate-x":[{"translate-x":[C]}],"translate-y":[{"translate-y":[C]}],"skew-x":[{"skew-x":[_]}],"skew-y":[{"skew-y":[_]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left",ie]}],accent:[{accent:["auto",e]}],appearance:[{appearance:["none","auto"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ie]}],"caret-color":[{caret:[e]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":M()}],"scroll-mx":[{"scroll-mx":M()}],"scroll-my":[{"scroll-my":M()}],"scroll-ms":[{"scroll-ms":M()}],"scroll-me":[{"scroll-me":M()}],"scroll-mt":[{"scroll-mt":M()}],"scroll-mr":[{"scroll-mr":M()}],"scroll-mb":[{"scroll-mb":M()}],"scroll-ml":[{"scroll-ml":M()}],"scroll-p":[{"scroll-p":M()}],"scroll-px":[{"scroll-px":M()}],"scroll-py":[{"scroll-py":M()}],"scroll-ps":[{"scroll-ps":M()}],"scroll-pe":[{"scroll-pe":M()}],"scroll-pt":[{"scroll-pt":M()}],"scroll-pr":[{"scroll-pr":M()}],"scroll-pb":[{"scroll-pb":M()}],"scroll-pl":[{"scroll-pl":M()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ie]}],fill:[{fill:[e,"none"]}],"stroke-w":[{stroke:[mn,qn,Th]}],stroke:[{stroke:[e,"none"]}],sr:["sr-only","not-sr-only"],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]}}},z6=w6(F6);function Me(...e){return z6(AN(e))}const B6=B4,FN=f.forwardRef(({className:e,...t},n)=>s.jsx(kN,{ref:n,className:Me("fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",e),...t}));FN.displayName=kN.displayName;const W6=mf("group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",{variants:{variant:{default:"border bg-background text-foreground",destructive:"destructive group border-destructive bg-destructive text-destructive-foreground"}},defaultVariants:{variant:"default"}}),zN=f.forwardRef(({className:e,variant:t,...n},r)=>s.jsx(_N,{ref:r,className:Me(W6({variant:t}),e),...n}));zN.displayName=_N.displayName;const U6=f.forwardRef(({className:e,...t},n)=>s.jsx(EN,{ref:n,className:Me("inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors group-[.destructive]:border-muted/40 hover:bg-secondary group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 group-[.destructive]:focus:ring-destructive disabled:pointer-events-none disabled:opacity-50",e),...t}));U6.displayName=EN.displayName;const BN=f.forwardRef(({className:e,...t},n)=>s.jsx(TN,{ref:n,className:Me("absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity group-hover:opacity-100 group-[.destructive]:text-red-300 hover:text-foreground group-[.destructive]:hover:text-red-50 focus:opacity-100 focus:outline-none focus:ring-2 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",e),"toast-close":"",...t,children:s.jsx($a,{className:"h-4 w-4"})}));BN.displayName=TN.displayName;const WN=f.forwardRef(({className:e,...t},n)=>s.jsx(CN,{ref:n,className:Me("text-sm font-semibold",e),...t}));WN.displayName=CN.displayName;const UN=f.forwardRef(({className:e,...t},n)=>s.jsx(SN,{ref:n,className:Me("text-sm opacity-90",e),...t}));UN.displayName=SN.displayName;function H6(){const{toasts:e}=K3();return s.jsxs(B6,{children:[e.map(function({id:t,title:n,description:r,action:o,...a}){return s.jsxs(zN,{...a,children:[s.jsxs("div",{className:"grid gap-1",children:[n&&s.jsx(WN,{children:n}),r&&s.jsx(UN,{children:r})]}),o,s.jsx(BN,{})]},t)}),s.jsx(FN,{})]})}var Qv=["light","dark"],q6="(prefers-color-scheme: dark)",V6=f.createContext(void 0),G6={setTheme:e=>{},themes:[]},Y6=()=>{var e;return(e=f.useContext(V6))!=null?e:G6};f.memo(({forcedTheme:e,storageKey:t,attribute:n,enableSystem:r,enableColorScheme:o,defaultTheme:a,value:i,attrs:l,nonce:c})=>{let d=a==="system",u=n==="class"?`var d=document.documentElement,c=d.classList;${`c.remove(${l.map(w=>`'${w}'`).join(",")})`};`:`var d=document.documentElement,n='${n}',s='setAttribute';`,h=o?Qv.includes(a)&&a?`if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${a}'`:"if(e==='light'||e==='dark')d.style.colorScheme=e":"",p=(w,x=!1,b=!0)=>{let v=i?i[w]:w,g=x?w+"|| ''":`'${v}'`,y="";return o&&b&&!x&&Qv.includes(w)&&(y+=`d.style.colorScheme = '${w}';`),n==="class"?x||v?y+=`c.add(${g})`:y+="null":v&&(y+=`d[s](n,${g})`),y},m=e?`!function(){${u}${p(e)}}()`:r?`!function(){try{${u}var e=localStorage.getItem('${t}');if('system'===e||(!e&&${d})){var t='${q6}',m=window.matchMedia(t);if(m.media!==t||m.matches){${p("dark")}}else{${p("light")}}}else if(e){${i?`var x=${JSON.stringify(i)};`:""}${p(i?"x[e]":"e",!0)}}${d?"":"else{"+p(a,!1,!1)+"}"}${h}}catch(e){}}()`:`!function(){try{${u}var e=localStorage.getItem('${t}');if(e){${i?`var x=${JSON.stringify(i)};`:""}${p(i?"x[e]":"e",!0)}}else{${p(a,!1,!1)};}${h}}catch(t){}}();`;return f.createElement("script",{nonce:c,dangerouslySetInnerHTML:{__html:m}})});var Q6=e=>{switch(e){case"success":return J6;case"info":return eT;case"warning":return Z6;case"error":return tT;default:return null}},K6=Array(12).fill(0),X6=({visible:e,className:t})=>R.createElement("div",{className:["sonner-loading-wrapper",t].filter(Boolean).join(" "),"data-visible":e},R.createElement("div",{className:"sonner-spinner"},K6.map((n,r)=>R.createElement("div",{className:"sonner-loading-bar",key:`spinner-bar-${r}`})))),J6=R.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},R.createElement("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z",clipRule:"evenodd"})),Z6=R.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",height:"20",width:"20"},R.createElement("path",{fillRule:"evenodd",d:"M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z",clipRule:"evenodd"})),eT=R.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},R.createElement("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z",clipRule:"evenodd"})),tT=R.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},R.createElement("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",clipRule:"evenodd"})),sT=R.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"},R.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),R.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"})),nT=()=>{let[e,t]=R.useState(document.hidden);return R.useEffect(()=>{let n=()=>{t(document.hidden)};return document.addEventListener("visibilitychange",n),()=>window.removeEventListener("visibilitychange",n)},[]),e},xp=1,rT=class{constructor(){this.subscribe=t=>(this.subscribers.push(t),()=>{let n=this.subscribers.indexOf(t);this.subscribers.splice(n,1)}),this.publish=t=>{this.subscribers.forEach(n=>n(t))},this.addToast=t=>{this.publish(t),this.toasts=[...this.toasts,t]},this.create=t=>{var n;let{message:r,...o}=t,a=typeof(t==null?void 0:t.id)=="number"||((n=t.id)==null?void 0:n.length)>0?t.id:xp++,i=this.toasts.find(c=>c.id===a),l=t.dismissible===void 0?!0:t.dismissible;return this.dismissedToasts.has(a)&&this.dismissedToasts.delete(a),i?this.toasts=this.toasts.map(c=>c.id===a?(this.publish({...c,...t,id:a,title:r}),{...c,...t,id:a,dismissible:l,title:r}):c):this.addToast({title:r,...o,dismissible:l,id:a}),a},this.dismiss=t=>(this.dismissedToasts.add(t),t||this.toasts.forEach(n=>{this.subscribers.forEach(r=>r({id:n.id,dismiss:!0}))}),this.subscribers.forEach(n=>n({id:t,dismiss:!0})),t),this.message=(t,n)=>this.create({...n,message:t}),this.error=(t,n)=>this.create({...n,message:t,type:"error"}),this.success=(t,n)=>this.create({...n,type:"success",message:t}),this.info=(t,n)=>this.create({...n,type:"info",message:t}),this.warning=(t,n)=>this.create({...n,type:"warning",message:t}),this.loading=(t,n)=>this.create({...n,type:"loading",message:t}),this.promise=(t,n)=>{if(!n)return;let r;n.loading!==void 0&&(r=this.create({...n,promise:t,type:"loading",message:n.loading,description:typeof n.description!="function"?n.description:void 0}));let o=t instanceof Promise?t:t(),a=r!==void 0,i,l=o.then(async d=>{if(i=["resolve",d],R.isValidElement(d))a=!1,this.create({id:r,type:"default",message:d});else if(aT(d)&&!d.ok){a=!1;let u=typeof n.error=="function"?await n.error(`HTTP error! status: ${d.status}`):n.error,h=typeof n.description=="function"?await n.description(`HTTP error! status: ${d.status}`):n.description;this.create({id:r,type:"error",message:u,description:h})}else if(n.success!==void 0){a=!1;let u=typeof n.success=="function"?await n.success(d):n.success,h=typeof n.description=="function"?await n.description(d):n.description;this.create({id:r,type:"success",message:u,description:h})}}).catch(async d=>{if(i=["reject",d],n.error!==void 0){a=!1;let u=typeof n.error=="function"?await n.error(d):n.error,h=typeof n.description=="function"?await n.description(d):n.description;this.create({id:r,type:"error",message:u,description:h})}}).finally(()=>{var d;a&&(this.dismiss(r),r=void 0),(d=n.finally)==null||d.call(n)}),c=()=>new Promise((d,u)=>l.then(()=>i[0]==="reject"?u(i[1]):d(i[1])).catch(u));return typeof r!="string"&&typeof r!="number"?{unwrap:c}:Object.assign(r,{unwrap:c})},this.custom=(t,n)=>{let r=(n==null?void 0:n.id)||xp++;return this.create({jsx:t(r),id:r,...n}),r},this.getActiveToasts=()=>this.toasts.filter(t=>!this.dismissedToasts.has(t.id)),this.subscribers=[],this.toasts=[],this.dismissedToasts=new Set}},zt=new rT,oT=(e,t)=>{let n=(t==null?void 0:t.id)||xp++;return zt.addToast({title:e,...t,id:n}),n},aT=e=>e&&typeof e=="object"&&"ok"in e&&typeof e.ok=="boolean"&&"status"in e&&typeof e.status=="number",iT=oT,lT=()=>zt.toasts,cT=()=>zt.getActiveToasts();Object.assign(iT,{success:zt.success,info:zt.info,warning:zt.warning,error:zt.error,custom:zt.custom,message:zt.message,promise:zt.promise,dismiss:zt.dismiss,loading:zt.loading},{getHistory:lT,getToasts:cT});function dT(e,{insertAt:t}={}){if(typeof document>"u")return;let n=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",t==="top"&&n.firstChild?n.insertBefore(r,n.firstChild):n.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}dT(`:where(html[dir="ltr"]),:where([data-sonner-toaster][dir="ltr"]){--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}:where(html[dir="rtl"]),:where([data-sonner-toaster][dir="rtl"]){--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}:where([data-sonner-toaster]){position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}:where([data-sonner-toaster][data-lifted="true"]){transform:translateY(-10px)}@media (hover: none) and (pointer: coarse){:where([data-sonner-toaster][data-lifted="true"]){transform:none}}:where([data-sonner-toaster][data-x-position="right"]){right:var(--offset-right)}:where([data-sonner-toaster][data-x-position="left"]){left:var(--offset-left)}:where([data-sonner-toaster][data-x-position="center"]){left:50%;transform:translate(-50%)}:where([data-sonner-toaster][data-y-position="top"]){top:var(--offset-top)}:where([data-sonner-toaster][data-y-position="bottom"]){bottom:var(--offset-bottom)}:where([data-sonner-toast]){--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);filter:blur(0);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}:where([data-sonner-toast][data-styled="true"]){padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}:where([data-sonner-toast]:focus-visible){box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast][data-y-position="top"]){top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}:where([data-sonner-toast][data-y-position="bottom"]){bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}:where([data-sonner-toast]) :where([data-description]){font-weight:400;line-height:1.4;color:inherit}:where([data-sonner-toast]) :where([data-title]){font-weight:500;line-height:1.5;color:inherit}:where([data-sonner-toast]) :where([data-icon]){display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}:where([data-sonner-toast][data-promise="true"]) :where([data-icon])>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}:where([data-sonner-toast]) :where([data-icon])>*{flex-shrink:0}:where([data-sonner-toast]) :where([data-icon]) svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}:where([data-sonner-toast]) :where([data-content]){display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}:where([data-sonner-toast]) :where([data-button]):focus-visible{box-shadow:0 0 0 2px #0006}:where([data-sonner-toast]) :where([data-button]):first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}:where([data-sonner-toast]) :where([data-cancel]){color:var(--normal-text);background:rgba(0,0,0,.08)}:where([data-sonner-toast][data-theme="dark"]) :where([data-cancel]){background:rgba(255,255,255,.3)}:where([data-sonner-toast]) :where([data-close-button]){position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast] [data-close-button]{background:var(--gray1)}:where([data-sonner-toast]) :where([data-close-button]):focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast]) :where([data-disabled="true"]){cursor:not-allowed}:where([data-sonner-toast]):hover :where([data-close-button]):hover{background:var(--gray2);border-color:var(--gray5)}:where([data-sonner-toast][data-swiping="true"]):before{content:"";position:absolute;left:-50%;right:-50%;height:100%;z-index:-1}:where([data-sonner-toast][data-y-position="top"][data-swiping="true"]):before{bottom:50%;transform:scaleY(3) translateY(50%)}:where([data-sonner-toast][data-y-position="bottom"][data-swiping="true"]):before{top:50%;transform:scaleY(3) translateY(-50%)}:where([data-sonner-toast][data-swiping="false"][data-removed="true"]):before{content:"";position:absolute;inset:0;transform:scaleY(2)}:where([data-sonner-toast]):after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}:where([data-sonner-toast][data-mounted="true"]){--y: translateY(0);opacity:1}:where([data-sonner-toast][data-expanded="false"][data-front="false"]){--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}:where([data-sonner-toast])>*{transition:opacity .4s}:where([data-sonner-toast][data-expanded="false"][data-front="false"][data-styled="true"])>*{opacity:0}:where([data-sonner-toast][data-visible="false"]){opacity:0;pointer-events:none}:where([data-sonner-toast][data-mounted="true"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}:where([data-sonner-toast][data-removed="true"][data-front="true"][data-swipe-out="false"]){--y: translateY(calc(var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="false"]){--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}:where([data-sonner-toast][data-removed="true"][data-front="false"]):before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width: 600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 91%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 91%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 91%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 100%, 12%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 12%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media (prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)} `);function rc(e){return e.label!==void 0}var uT=3,hT="32px",mT="16px",Kv=4e3,pT=356,xT=14,fT=20,gT=200;function Es(...e){return e.filter(Boolean).join(" ")}function vT(e){let[t,n]=e.split("-"),r=[];return t&&r.push(t),n&&r.push(n),r}var yT=e=>{var t,n,r,o,a,i,l,c,d,u,h;let{invert:p,toast:m,unstyled:w,interacting:x,setHeights:b,visibleToasts:v,heights:g,index:y,toasts:j,expanded:N,removeToast:_,defaultRichColors:k,closeButton:C,style:P,cancelButtonStyle:A,actionButtonStyle:H,className:M="",descriptionClassName:K="",duration:O,position:X,gap:q,loadingIcon:Y,expandByDefault:E,classNames:T,icons:$,closeButtonAriaLabel:Z="Close toast",pauseWhenPageIsHidden:W}=e,[ee,J]=R.useState(null),[we,Ne]=R.useState(null),[F,ke]=R.useState(!1),[Ue,ce]=R.useState(!1),[xe,fe]=R.useState(!1),[tt,Et]=R.useState(!1),[qs,js]=R.useState(!1),[Ns,Va]=R.useState(0),[No,dg]=R.useState(0),Ga=R.useRef(m.duration||O||Kv),ug=R.useRef(null),$r=R.useRef(null),uC=y===0,hC=y+1<=v,hs=m.type,ko=m.dismissible!==!1,mC=m.className||"",pC=m.descriptionClassName||"",$l=R.useMemo(()=>g.findIndex(re=>re.toastId===m.id)||0,[g,m.id]),xC=R.useMemo(()=>{var re;return(re=m.closeButton)!=null?re:C},[m.closeButton,C]),hg=R.useMemo(()=>m.duration||O||Kv,[m.duration,O]),Ku=R.useRef(0),_o=R.useRef(0),mg=R.useRef(0),Co=R.useRef(null),[fC,gC]=X.split("-"),pg=R.useMemo(()=>g.reduce((re,Te,He)=>He>=$l?re:re+Te.height,0),[g,$l]),xg=nT(),vC=m.invert||p,Xu=hs==="loading";_o.current=R.useMemo(()=>$l*q+pg,[$l,pg]),R.useEffect(()=>{Ga.current=hg},[hg]),R.useEffect(()=>{ke(!0)},[]),R.useEffect(()=>{let re=$r.current;if(re){let Te=re.getBoundingClientRect().height;return dg(Te),b(He=>[{toastId:m.id,height:Te,position:m.position},...He]),()=>b(He=>He.filter(ks=>ks.toastId!==m.id))}},[b,m.id]),R.useLayoutEffect(()=>{if(!F)return;let re=$r.current,Te=re.style.height;re.style.height="auto";let He=re.getBoundingClientRect().height;re.style.height=Te,dg(He),b(ks=>ks.find(_s=>_s.toastId===m.id)?ks.map(_s=>_s.toastId===m.id?{..._s,height:He}:_s):[{toastId:m.id,height:He,position:m.position},...ks])},[F,m.title,m.description,b,m.id]);let Bn=R.useCallback(()=>{ce(!0),Va(_o.current),b(re=>re.filter(Te=>Te.toastId!==m.id)),setTimeout(()=>{_(m)},gT)},[m,_,b,_o]);R.useEffect(()=>{if(m.promise&&hs==="loading"||m.duration===1/0||m.type==="loading")return;let re;return N||x||W&&xg?(()=>{if(mg.current{var Te;(Te=m.onAutoClose)==null||Te.call(m,m),Bn()},Ga.current)),()=>clearTimeout(re)},[N,x,m,hs,W,xg,Bn]),R.useEffect(()=>{m.delete&&Bn()},[Bn,m.delete]);function yC(){var re,Te,He;return $!=null&&$.loading?R.createElement("div",{className:Es(T==null?void 0:T.loader,(re=m==null?void 0:m.classNames)==null?void 0:re.loader,"sonner-loader"),"data-visible":hs==="loading"},$.loading):Y?R.createElement("div",{className:Es(T==null?void 0:T.loader,(Te=m==null?void 0:m.classNames)==null?void 0:Te.loader,"sonner-loader"),"data-visible":hs==="loading"},Y):R.createElement(X6,{className:Es(T==null?void 0:T.loader,(He=m==null?void 0:m.classNames)==null?void 0:He.loader),visible:hs==="loading"})}return R.createElement("li",{tabIndex:0,ref:$r,className:Es(M,mC,T==null?void 0:T.toast,(t=m==null?void 0:m.classNames)==null?void 0:t.toast,T==null?void 0:T.default,T==null?void 0:T[hs],(n=m==null?void 0:m.classNames)==null?void 0:n[hs]),"data-sonner-toast":"","data-rich-colors":(r=m.richColors)!=null?r:k,"data-styled":!(m.jsx||m.unstyled||w),"data-mounted":F,"data-promise":!!m.promise,"data-swiped":qs,"data-removed":Ue,"data-visible":hC,"data-y-position":fC,"data-x-position":gC,"data-index":y,"data-front":uC,"data-swiping":xe,"data-dismissible":ko,"data-type":hs,"data-invert":vC,"data-swipe-out":tt,"data-swipe-direction":we,"data-expanded":!!(N||E&&F),style:{"--index":y,"--toasts-before":y,"--z-index":j.length-y,"--offset":`${Ue?Ns:_o.current}px`,"--initial-height":E?"auto":`${No}px`,...P,...m.style},onDragEnd:()=>{fe(!1),J(null),Co.current=null},onPointerDown:re=>{Xu||!ko||(ug.current=new Date,Va(_o.current),re.target.setPointerCapture(re.pointerId),re.target.tagName!=="BUTTON"&&(fe(!0),Co.current={x:re.clientX,y:re.clientY}))},onPointerUp:()=>{var re,Te,He,ks;if(tt||!ko)return;Co.current=null;let _s=Number(((re=$r.current)==null?void 0:re.style.getPropertyValue("--swipe-amount-x").replace("px",""))||0),Wn=Number(((Te=$r.current)==null?void 0:Te.style.getPropertyValue("--swipe-amount-y").replace("px",""))||0),Lr=new Date().getTime()-((He=ug.current)==null?void 0:He.getTime()),Cs=ee==="x"?_s:Wn,Un=Math.abs(Cs)/Lr;if(Math.abs(Cs)>=fT||Un>.11){Va(_o.current),(ks=m.onDismiss)==null||ks.call(m,m),Ne(ee==="x"?_s>0?"right":"left":Wn>0?"down":"up"),Bn(),Et(!0),js(!1);return}fe(!1),J(null)},onPointerMove:re=>{var Te,He,ks,_s;if(!Co.current||!ko||((Te=window.getSelection())==null?void 0:Te.toString().length)>0)return;let Wn=re.clientY-Co.current.y,Lr=re.clientX-Co.current.x,Cs=(He=e.swipeDirections)!=null?He:vT(X);!ee&&(Math.abs(Lr)>1||Math.abs(Wn)>1)&&J(Math.abs(Lr)>Math.abs(Wn)?"x":"y");let Un={x:0,y:0};ee==="y"?(Cs.includes("top")||Cs.includes("bottom"))&&(Cs.includes("top")&&Wn<0||Cs.includes("bottom")&&Wn>0)&&(Un.y=Wn):ee==="x"&&(Cs.includes("left")||Cs.includes("right"))&&(Cs.includes("left")&&Lr<0||Cs.includes("right")&&Lr>0)&&(Un.x=Lr),(Math.abs(Un.x)>0||Math.abs(Un.y)>0)&&js(!0),(ks=$r.current)==null||ks.style.setProperty("--swipe-amount-x",`${Un.x}px`),(_s=$r.current)==null||_s.style.setProperty("--swipe-amount-y",`${Un.y}px`)}},xC&&!m.jsx?R.createElement("button",{"aria-label":Z,"data-disabled":Xu,"data-close-button":!0,onClick:Xu||!ko?()=>{}:()=>{var re;Bn(),(re=m.onDismiss)==null||re.call(m,m)},className:Es(T==null?void 0:T.closeButton,(o=m==null?void 0:m.classNames)==null?void 0:o.closeButton)},(a=$==null?void 0:$.close)!=null?a:sT):null,m.jsx||f.isValidElement(m.title)?m.jsx?m.jsx:typeof m.title=="function"?m.title():m.title:R.createElement(R.Fragment,null,hs||m.icon||m.promise?R.createElement("div",{"data-icon":"",className:Es(T==null?void 0:T.icon,(i=m==null?void 0:m.classNames)==null?void 0:i.icon)},m.promise||m.type==="loading"&&!m.icon?m.icon||yC():null,m.type!=="loading"?m.icon||($==null?void 0:$[hs])||Q6(hs):null):null,R.createElement("div",{"data-content":"",className:Es(T==null?void 0:T.content,(l=m==null?void 0:m.classNames)==null?void 0:l.content)},R.createElement("div",{"data-title":"",className:Es(T==null?void 0:T.title,(c=m==null?void 0:m.classNames)==null?void 0:c.title)},typeof m.title=="function"?m.title():m.title),m.description?R.createElement("div",{"data-description":"",className:Es(K,pC,T==null?void 0:T.description,(d=m==null?void 0:m.classNames)==null?void 0:d.description)},typeof m.description=="function"?m.description():m.description):null),f.isValidElement(m.cancel)?m.cancel:m.cancel&&rc(m.cancel)?R.createElement("button",{"data-button":!0,"data-cancel":!0,style:m.cancelButtonStyle||A,onClick:re=>{var Te,He;rc(m.cancel)&&ko&&((He=(Te=m.cancel).onClick)==null||He.call(Te,re),Bn())},className:Es(T==null?void 0:T.cancelButton,(u=m==null?void 0:m.classNames)==null?void 0:u.cancelButton)},m.cancel.label):null,f.isValidElement(m.action)?m.action:m.action&&rc(m.action)?R.createElement("button",{"data-button":!0,"data-action":!0,style:m.actionButtonStyle||H,onClick:re=>{var Te,He;rc(m.action)&&((He=(Te=m.action).onClick)==null||He.call(Te,re),!re.defaultPrevented&&Bn())},className:Es(T==null?void 0:T.actionButton,(h=m==null?void 0:m.classNames)==null?void 0:h.actionButton)},m.action.label):null))};function Xv(){if(typeof window>"u"||typeof document>"u")return"ltr";let e=document.documentElement.getAttribute("dir");return e==="auto"||!e?window.getComputedStyle(document.documentElement).direction:e}function bT(e,t){let n={};return[e,t].forEach((r,o)=>{let a=o===1,i=a?"--mobile-offset":"--offset",l=a?mT:hT;function c(d){["top","right","bottom","left"].forEach(u=>{n[`${i}-${u}`]=typeof d=="number"?`${d}px`:d})}typeof r=="number"||typeof r=="string"?c(r):typeof r=="object"?["top","right","bottom","left"].forEach(d=>{r[d]===void 0?n[`${i}-${d}`]=l:n[`${i}-${d}`]=typeof r[d]=="number"?`${r[d]}px`:r[d]}):c(l)}),n}var wT=f.forwardRef(function(e,t){let{invert:n,position:r="bottom-right",hotkey:o=["altKey","KeyT"],expand:a,closeButton:i,className:l,offset:c,mobileOffset:d,theme:u="light",richColors:h,duration:p,style:m,visibleToasts:w=uT,toastOptions:x,dir:b=Xv(),gap:v=xT,loadingIcon:g,icons:y,containerAriaLabel:j="Notifications",pauseWhenPageIsHidden:N}=e,[_,k]=R.useState([]),C=R.useMemo(()=>Array.from(new Set([r].concat(_.filter(W=>W.position).map(W=>W.position)))),[_,r]),[P,A]=R.useState([]),[H,M]=R.useState(!1),[K,O]=R.useState(!1),[X,q]=R.useState(u!=="system"?u:typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),Y=R.useRef(null),E=o.join("+").replace(/Key/g,"").replace(/Digit/g,""),T=R.useRef(null),$=R.useRef(!1),Z=R.useCallback(W=>{k(ee=>{var J;return(J=ee.find(we=>we.id===W.id))!=null&&J.delete||zt.dismiss(W.id),ee.filter(({id:we})=>we!==W.id)})},[]);return R.useEffect(()=>zt.subscribe(W=>{if(W.dismiss){k(ee=>ee.map(J=>J.id===W.id?{...J,delete:!0}:J));return}setTimeout(()=>{q0.flushSync(()=>{k(ee=>{let J=ee.findIndex(we=>we.id===W.id);return J!==-1?[...ee.slice(0,J),{...ee[J],...W},...ee.slice(J+1)]:[W,...ee]})})})}),[]),R.useEffect(()=>{if(u!=="system"){q(u);return}if(u==="system"&&(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?q("dark"):q("light")),typeof window>"u")return;let W=window.matchMedia("(prefers-color-scheme: dark)");try{W.addEventListener("change",({matches:ee})=>{q(ee?"dark":"light")})}catch{W.addListener(({matches:J})=>{try{q(J?"dark":"light")}catch(we){console.error(we)}})}},[u]),R.useEffect(()=>{_.length<=1&&M(!1)},[_]),R.useEffect(()=>{let W=ee=>{var J,we;o.every(Ne=>ee[Ne]||ee.code===Ne)&&(M(!0),(J=Y.current)==null||J.focus()),ee.code==="Escape"&&(document.activeElement===Y.current||(we=Y.current)!=null&&we.contains(document.activeElement))&&M(!1)};return document.addEventListener("keydown",W),()=>document.removeEventListener("keydown",W)},[o]),R.useEffect(()=>{if(Y.current)return()=>{T.current&&(T.current.focus({preventScroll:!0}),T.current=null,$.current=!1)}},[Y.current]),R.createElement("section",{ref:t,"aria-label":`${j} ${E}`,tabIndex:-1,"aria-live":"polite","aria-relevant":"additions text","aria-atomic":"false",suppressHydrationWarning:!0},C.map((W,ee)=>{var J;let[we,Ne]=W.split("-");return _.length?R.createElement("ol",{key:W,dir:b==="auto"?Xv():b,tabIndex:-1,ref:Y,className:l,"data-sonner-toaster":!0,"data-theme":X,"data-y-position":we,"data-lifted":H&&_.length>1&&!a,"data-x-position":Ne,style:{"--front-toast-height":`${((J=P[0])==null?void 0:J.height)||0}px`,"--width":`${pT}px`,"--gap":`${v}px`,...m,...bT(c,d)},onBlur:F=>{$.current&&!F.currentTarget.contains(F.relatedTarget)&&($.current=!1,T.current&&(T.current.focus({preventScroll:!0}),T.current=null))},onFocus:F=>{F.target instanceof HTMLElement&&F.target.dataset.dismissible==="false"||$.current||($.current=!0,T.current=F.relatedTarget)},onMouseEnter:()=>M(!0),onMouseMove:()=>M(!0),onMouseLeave:()=>{K||M(!1)},onDragEnd:()=>M(!1),onPointerDown:F=>{F.target instanceof HTMLElement&&F.target.dataset.dismissible==="false"||O(!0)},onPointerUp:()=>O(!1)},_.filter(F=>!F.position&&ee===0||F.position===W).map((F,ke)=>{var Ue,ce;return R.createElement(yT,{key:F.id,icons:y,index:ke,toast:F,defaultRichColors:h,duration:(Ue=x==null?void 0:x.duration)!=null?Ue:p,className:x==null?void 0:x.className,descriptionClassName:x==null?void 0:x.descriptionClassName,invert:n,visibleToasts:w,closeButton:(ce=x==null?void 0:x.closeButton)!=null?ce:i,interacting:K,position:W,style:x==null?void 0:x.style,unstyled:x==null?void 0:x.unstyled,classNames:x==null?void 0:x.classNames,cancelButtonStyle:x==null?void 0:x.cancelButtonStyle,actionButtonStyle:x==null?void 0:x.actionButtonStyle,removeToast:Z,toasts:_.filter(xe=>xe.position==F.position),heights:P.filter(xe=>xe.position==F.position),setHeights:A,expandByDefault:a,gap:v,loadingIcon:g,expanded:H,pauseWhenPageIsHidden:N,swipeDirections:e.swipeDirections})})):null}))});const jT=({...e})=>{const{theme:t="system"}=Y6();return s.jsx(wT,{theme:t,className:"toaster group",toastOptions:{classNames:{toast:"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",description:"group-[.toast]:text-muted-foreground",actionButton:"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",cancelButton:"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground"}},...e})};var NT=px[" useId ".trim().toString()]||(()=>{}),kT=0;function na(e){const[t,n]=f.useState(NT());return vt(()=>{n(r=>r??String(kT++))},[e]),t?`radix-${t}`:""}const _T=["top","right","bottom","left"],Tr=Math.min,rs=Math.max,_d=Math.round,oc=Math.floor,ln=e=>({x:e,y:e}),CT={left:"right",right:"left",bottom:"top",top:"bottom"},ST={start:"end",end:"start"};function fp(e,t,n){return rs(e,Tr(t,n))}function On(e,t){return typeof e=="function"?e(t):e}function Dn(e){return e.split("-")[0]}function Fa(e){return e.split("-")[1]}function xf(e){return e==="x"?"y":"x"}function ff(e){return e==="y"?"height":"width"}const ET=new Set(["top","bottom"]);function sn(e){return ET.has(Dn(e))?"y":"x"}function gf(e){return xf(sn(e))}function TT(e,t,n){n===void 0&&(n=!1);const r=Fa(e),o=gf(e),a=ff(o);let i=o==="x"?r===(n?"end":"start")?"right":"left":r==="start"?"bottom":"top";return t.reference[a]>t.floating[a]&&(i=Cd(i)),[i,Cd(i)]}function IT(e){const t=Cd(e);return[gp(e),t,gp(t)]}function gp(e){return e.replace(/start|end/g,t=>ST[t])}const Jv=["left","right"],Zv=["right","left"],AT=["top","bottom"],PT=["bottom","top"];function OT(e,t,n){switch(e){case"top":case"bottom":return n?t?Zv:Jv:t?Jv:Zv;case"left":case"right":return t?AT:PT;default:return[]}}function DT(e,t,n,r){const o=Fa(e);let a=OT(Dn(e),n==="start",r);return o&&(a=a.map(i=>i+"-"+o),t&&(a=a.concat(a.map(gp)))),a}function Cd(e){return e.replace(/left|right|bottom|top/g,t=>CT[t])}function RT(e){return{top:0,right:0,bottom:0,left:0,...e}}function HN(e){return typeof e!="number"?RT(e):{top:e,right:e,bottom:e,left:e}}function Sd(e){const{x:t,y:n,width:r,height:o}=e;return{width:r,height:o,top:n,left:t,right:t+r,bottom:n+o,x:t,y:n}}function ey(e,t,n){let{reference:r,floating:o}=e;const a=sn(t),i=gf(t),l=ff(i),c=Dn(t),d=a==="y",u=r.x+r.width/2-o.width/2,h=r.y+r.height/2-o.height/2,p=r[l]/2-o[l]/2;let m;switch(c){case"top":m={x:u,y:r.y-o.height};break;case"bottom":m={x:u,y:r.y+r.height};break;case"right":m={x:r.x+r.width,y:h};break;case"left":m={x:r.x-o.width,y:h};break;default:m={x:r.x,y:r.y}}switch(Fa(t)){case"start":m[i]-=p*(n&&d?-1:1);break;case"end":m[i]+=p*(n&&d?-1:1);break}return m}const MT=async(e,t,n)=>{const{placement:r="bottom",strategy:o="absolute",middleware:a=[],platform:i}=n,l=a.filter(Boolean),c=await(i.isRTL==null?void 0:i.isRTL(t));let d=await i.getElementRects({reference:e,floating:t,strategy:o}),{x:u,y:h}=ey(d,r,c),p=r,m={},w=0;for(let x=0;x({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:o,rects:a,platform:i,elements:l,middlewareData:c}=t,{element:d,padding:u=0}=On(e,t)||{};if(d==null)return{};const h=HN(u),p={x:n,y:r},m=gf(o),w=ff(m),x=await i.getDimensions(d),b=m==="y",v=b?"top":"left",g=b?"bottom":"right",y=b?"clientHeight":"clientWidth",j=a.reference[w]+a.reference[m]-p[m]-a.floating[w],N=p[m]-a.reference[m],_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(d));let k=_?_[y]:0;(!k||!await(i.isElement==null?void 0:i.isElement(_)))&&(k=l.floating[y]||a.floating[w]);const C=j/2-N/2,P=k/2-x[w]/2-1,A=Tr(h[v],P),H=Tr(h[g],P),M=A,K=k-x[w]-H,O=k/2-x[w]/2+C,X=fp(M,O,K),q=!c.arrow&&Fa(o)!=null&&O!==X&&a.reference[w]/2-(OO<=0)){var H,M;const O=(((H=a.flip)==null?void 0:H.index)||0)+1,X=k[O];if(X&&(!(h==="alignment"?g!==sn(X):!1)||A.every(E=>E.overflows[0]>0&&sn(E.placement)===g)))return{data:{index:O,overflows:A},reset:{placement:X}};let q=(M=A.filter(Y=>Y.overflows[0]<=0).sort((Y,E)=>Y.overflows[1]-E.overflows[1])[0])==null?void 0:M.placement;if(!q)switch(m){case"bestFit":{var K;const Y=(K=A.filter(E=>{if(_){const T=sn(E.placement);return T===g||T==="y"}return!0}).map(E=>[E.placement,E.overflows.filter(T=>T>0).reduce((T,$)=>T+$,0)]).sort((E,T)=>E[1]-T[1])[0])==null?void 0:K[0];Y&&(q=Y);break}case"initialPlacement":q=l;break}if(o!==q)return{reset:{placement:q}}}return{}}}};function ty(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function sy(e){return _T.some(t=>e[t]>=0)}const FT=function(e){return e===void 0&&(e={}),{name:"hide",options:e,async fn(t){const{rects:n}=t,{strategy:r="referenceHidden",...o}=On(e,t);switch(r){case"referenceHidden":{const a=await cl(t,{...o,elementContext:"reference"}),i=ty(a,n.reference);return{data:{referenceHiddenOffsets:i,referenceHidden:sy(i)}}}case"escaped":{const a=await cl(t,{...o,altBoundary:!0}),i=ty(a,n.floating);return{data:{escapedOffsets:i,escaped:sy(i)}}}default:return{}}}}},qN=new Set(["left","top"]);async function zT(e,t){const{placement:n,platform:r,elements:o}=e,a=await(r.isRTL==null?void 0:r.isRTL(o.floating)),i=Dn(n),l=Fa(n),c=sn(n)==="y",d=qN.has(i)?-1:1,u=a&&c?-1:1,h=On(t,e);let{mainAxis:p,crossAxis:m,alignmentAxis:w}=typeof h=="number"?{mainAxis:h,crossAxis:0,alignmentAxis:null}:{mainAxis:h.mainAxis||0,crossAxis:h.crossAxis||0,alignmentAxis:h.alignmentAxis};return l&&typeof w=="number"&&(m=l==="end"?w*-1:w),c?{x:m*u,y:p*d}:{x:p*d,y:m*u}}const BT=function(e){return e===void 0&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:o,y:a,placement:i,middlewareData:l}=t,c=await zT(t,e);return i===((n=l.offset)==null?void 0:n.placement)&&(r=l.arrow)!=null&&r.alignmentOffset?{}:{x:o+c.x,y:a+c.y,data:{...c,placement:i}}}}},WT=function(e){return e===void 0&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:o}=t,{mainAxis:a=!0,crossAxis:i=!1,limiter:l={fn:b=>{let{x:v,y:g}=b;return{x:v,y:g}}},...c}=On(e,t),d={x:n,y:r},u=await cl(t,c),h=sn(Dn(o)),p=xf(h);let m=d[p],w=d[h];if(a){const b=p==="y"?"top":"left",v=p==="y"?"bottom":"right",g=m+u[b],y=m-u[v];m=fp(g,m,y)}if(i){const b=h==="y"?"top":"left",v=h==="y"?"bottom":"right",g=w+u[b],y=w-u[v];w=fp(g,w,y)}const x=l.fn({...t,[p]:m,[h]:w});return{...x,data:{x:x.x-n,y:x.y-r,enabled:{[p]:a,[h]:i}}}}}},UT=function(e){return e===void 0&&(e={}),{options:e,fn(t){const{x:n,y:r,placement:o,rects:a,middlewareData:i}=t,{offset:l=0,mainAxis:c=!0,crossAxis:d=!0}=On(e,t),u={x:n,y:r},h=sn(o),p=xf(h);let m=u[p],w=u[h];const x=On(l,t),b=typeof x=="number"?{mainAxis:x,crossAxis:0}:{mainAxis:0,crossAxis:0,...x};if(c){const y=p==="y"?"height":"width",j=a.reference[p]-a.floating[y]+b.mainAxis,N=a.reference[p]+a.reference[y]-b.mainAxis;mN&&(m=N)}if(d){var v,g;const y=p==="y"?"width":"height",j=qN.has(Dn(o)),N=a.reference[h]-a.floating[y]+(j&&((v=i.offset)==null?void 0:v[h])||0)+(j?0:b.crossAxis),_=a.reference[h]+a.reference[y]+(j?0:((g=i.offset)==null?void 0:g[h])||0)-(j?b.crossAxis:0);w_&&(w=_)}return{[p]:m,[h]:w}}}},HT=function(e){return e===void 0&&(e={}),{name:"size",options:e,async fn(t){var n,r;const{placement:o,rects:a,platform:i,elements:l}=t,{apply:c=()=>{},...d}=On(e,t),u=await cl(t,d),h=Dn(o),p=Fa(o),m=sn(o)==="y",{width:w,height:x}=a.floating;let b,v;h==="top"||h==="bottom"?(b=h,v=p===(await(i.isRTL==null?void 0:i.isRTL(l.floating))?"start":"end")?"left":"right"):(v=h,b=p==="end"?"top":"bottom");const g=x-u.top-u.bottom,y=w-u.left-u.right,j=Tr(x-u[b],g),N=Tr(w-u[v],y),_=!t.middlewareData.shift;let k=j,C=N;if((n=t.middlewareData.shift)!=null&&n.enabled.x&&(C=y),(r=t.middlewareData.shift)!=null&&r.enabled.y&&(k=g),_&&!p){const A=rs(u.left,0),H=rs(u.right,0),M=rs(u.top,0),K=rs(u.bottom,0);m?C=w-2*(A!==0||H!==0?A+H:rs(u.left,u.right)):k=x-2*(M!==0||K!==0?M+K:rs(u.top,u.bottom))}await c({...t,availableWidth:C,availableHeight:k});const P=await i.getDimensions(l.floating);return w!==P.width||x!==P.height?{reset:{rects:!0}}:{}}}};function Nu(){return typeof window<"u"}function za(e){return VN(e)?(e.nodeName||"").toLowerCase():"#document"}function ls(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function hn(e){var t;return(t=(VN(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function VN(e){return Nu()?e instanceof Node||e instanceof ls(e).Node:!1}function Ws(e){return Nu()?e instanceof Element||e instanceof ls(e).Element:!1}function dn(e){return Nu()?e instanceof HTMLElement||e instanceof ls(e).HTMLElement:!1}function ny(e){return!Nu()||typeof ShadowRoot>"u"?!1:e instanceof ShadowRoot||e instanceof ls(e).ShadowRoot}const qT=new Set(["inline","contents"]);function Pl(e){const{overflow:t,overflowX:n,overflowY:r,display:o}=Us(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!qT.has(o)}const VT=new Set(["table","td","th"]);function GT(e){return VT.has(za(e))}const YT=[":popover-open",":modal"];function ku(e){return YT.some(t=>{try{return e.matches(t)}catch{return!1}})}const QT=["transform","translate","scale","rotate","perspective"],KT=["transform","translate","scale","rotate","perspective","filter"],XT=["paint","layout","strict","content"];function vf(e){const t=yf(),n=Ws(e)?Us(e):e;return QT.some(r=>n[r]?n[r]!=="none":!1)||(n.containerType?n.containerType!=="normal":!1)||!t&&(n.backdropFilter?n.backdropFilter!=="none":!1)||!t&&(n.filter?n.filter!=="none":!1)||KT.some(r=>(n.willChange||"").includes(r))||XT.some(r=>(n.contain||"").includes(r))}function JT(e){let t=Ir(e);for(;dn(t)&&!_a(t);){if(vf(t))return t;if(ku(t))return null;t=Ir(t)}return null}function yf(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}const ZT=new Set(["html","body","#document"]);function _a(e){return ZT.has(za(e))}function Us(e){return ls(e).getComputedStyle(e)}function _u(e){return Ws(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function Ir(e){if(za(e)==="html")return e;const t=e.assignedSlot||e.parentNode||ny(e)&&e.host||hn(e);return ny(t)?t.host:t}function GN(e){const t=Ir(e);return _a(t)?e.ownerDocument?e.ownerDocument.body:e.body:dn(t)&&Pl(t)?t:GN(t)}function dl(e,t,n){var r;t===void 0&&(t=[]),n===void 0&&(n=!0);const o=GN(e),a=o===((r=e.ownerDocument)==null?void 0:r.body),i=ls(o);if(a){const l=vp(i);return t.concat(i,i.visualViewport||[],Pl(o)?o:[],l&&n?dl(l):[])}return t.concat(o,dl(o,[],n))}function vp(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function YN(e){const t=Us(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const o=dn(e),a=o?e.offsetWidth:n,i=o?e.offsetHeight:r,l=_d(n)!==a||_d(r)!==i;return l&&(n=a,r=i),{width:n,height:r,$:l}}function bf(e){return Ws(e)?e:e.contextElement}function ra(e){const t=bf(e);if(!dn(t))return ln(1);const n=t.getBoundingClientRect(),{width:r,height:o,$:a}=YN(t);let i=(a?_d(n.width):n.width)/r,l=(a?_d(n.height):n.height)/o;return(!i||!Number.isFinite(i))&&(i=1),(!l||!Number.isFinite(l))&&(l=1),{x:i,y:l}}const eI=ln(0);function QN(e){const t=ls(e);return!yf()||!t.visualViewport?eI:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function tI(e,t,n){return t===void 0&&(t=!1),!n||t&&n!==ls(e)?!1:t}function mo(e,t,n,r){t===void 0&&(t=!1),n===void 0&&(n=!1);const o=e.getBoundingClientRect(),a=bf(e);let i=ln(1);t&&(r?Ws(r)&&(i=ra(r)):i=ra(e));const l=tI(a,n,r)?QN(a):ln(0);let c=(o.left+l.x)/i.x,d=(o.top+l.y)/i.y,u=o.width/i.x,h=o.height/i.y;if(a){const p=ls(a),m=r&&Ws(r)?ls(r):r;let w=p,x=vp(w);for(;x&&r&&m!==w;){const b=ra(x),v=x.getBoundingClientRect(),g=Us(x),y=v.left+(x.clientLeft+parseFloat(g.paddingLeft))*b.x,j=v.top+(x.clientTop+parseFloat(g.paddingTop))*b.y;c*=b.x,d*=b.y,u*=b.x,h*=b.y,c+=y,d+=j,w=ls(x),x=vp(w)}}return Sd({width:u,height:h,x:c,y:d})}function wf(e,t){const n=_u(e).scrollLeft;return t?t.left+n:mo(hn(e)).left+n}function KN(e,t,n){n===void 0&&(n=!1);const r=e.getBoundingClientRect(),o=r.left+t.scrollLeft-(n?0:wf(e,r)),a=r.top+t.scrollTop;return{x:o,y:a}}function sI(e){let{elements:t,rect:n,offsetParent:r,strategy:o}=e;const a=o==="fixed",i=hn(r),l=t?ku(t.floating):!1;if(r===i||l&&a)return n;let c={scrollLeft:0,scrollTop:0},d=ln(1);const u=ln(0),h=dn(r);if((h||!h&&!a)&&((za(r)!=="body"||Pl(i))&&(c=_u(r)),dn(r))){const m=mo(r);d=ra(r),u.x=m.x+r.clientLeft,u.y=m.y+r.clientTop}const p=i&&!h&&!a?KN(i,c,!0):ln(0);return{width:n.width*d.x,height:n.height*d.y,x:n.x*d.x-c.scrollLeft*d.x+u.x+p.x,y:n.y*d.y-c.scrollTop*d.y+u.y+p.y}}function nI(e){return Array.from(e.getClientRects())}function rI(e){const t=hn(e),n=_u(e),r=e.ownerDocument.body,o=rs(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),a=rs(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let i=-n.scrollLeft+wf(e);const l=-n.scrollTop;return Us(r).direction==="rtl"&&(i+=rs(t.clientWidth,r.clientWidth)-o),{width:o,height:a,x:i,y:l}}function oI(e,t){const n=ls(e),r=hn(e),o=n.visualViewport;let a=r.clientWidth,i=r.clientHeight,l=0,c=0;if(o){a=o.width,i=o.height;const d=yf();(!d||d&&t==="fixed")&&(l=o.offsetLeft,c=o.offsetTop)}return{width:a,height:i,x:l,y:c}}const aI=new Set(["absolute","fixed"]);function iI(e,t){const n=mo(e,!0,t==="fixed"),r=n.top+e.clientTop,o=n.left+e.clientLeft,a=dn(e)?ra(e):ln(1),i=e.clientWidth*a.x,l=e.clientHeight*a.y,c=o*a.x,d=r*a.y;return{width:i,height:l,x:c,y:d}}function ry(e,t,n){let r;if(t==="viewport")r=oI(e,n);else if(t==="document")r=rI(hn(e));else if(Ws(t))r=iI(t,n);else{const o=QN(e);r={x:t.x-o.x,y:t.y-o.y,width:t.width,height:t.height}}return Sd(r)}function XN(e,t){const n=Ir(e);return n===t||!Ws(n)||_a(n)?!1:Us(n).position==="fixed"||XN(n,t)}function lI(e,t){const n=t.get(e);if(n)return n;let r=dl(e,[],!1).filter(l=>Ws(l)&&za(l)!=="body"),o=null;const a=Us(e).position==="fixed";let i=a?Ir(e):e;for(;Ws(i)&&!_a(i);){const l=Us(i),c=vf(i);!c&&l.position==="fixed"&&(o=null),(a?!c&&!o:!c&&l.position==="static"&&!!o&&aI.has(o.position)||Pl(i)&&!c&&XN(e,i))?r=r.filter(u=>u!==i):o=l,i=Ir(i)}return t.set(e,r),r}function cI(e){let{element:t,boundary:n,rootBoundary:r,strategy:o}=e;const i=[...n==="clippingAncestors"?ku(t)?[]:lI(t,this._c):[].concat(n),r],l=i[0],c=i.reduce((d,u)=>{const h=ry(t,u,o);return d.top=rs(h.top,d.top),d.right=Tr(h.right,d.right),d.bottom=Tr(h.bottom,d.bottom),d.left=rs(h.left,d.left),d},ry(t,l,o));return{width:c.right-c.left,height:c.bottom-c.top,x:c.left,y:c.top}}function dI(e){const{width:t,height:n}=YN(e);return{width:t,height:n}}function uI(e,t,n){const r=dn(t),o=hn(t),a=n==="fixed",i=mo(e,!0,a,t);let l={scrollLeft:0,scrollTop:0};const c=ln(0);function d(){c.x=wf(o)}if(r||!r&&!a)if((za(t)!=="body"||Pl(o))&&(l=_u(t)),r){const m=mo(t,!0,a,t);c.x=m.x+t.clientLeft,c.y=m.y+t.clientTop}else o&&d();a&&!r&&o&&d();const u=o&&!r&&!a?KN(o,l):ln(0),h=i.left+l.scrollLeft-c.x-u.x,p=i.top+l.scrollTop-c.y-u.y;return{x:h,y:p,width:i.width,height:i.height}}function Ih(e){return Us(e).position==="static"}function oy(e,t){if(!dn(e)||Us(e).position==="fixed")return null;if(t)return t(e);let n=e.offsetParent;return hn(e)===n&&(n=n.ownerDocument.body),n}function JN(e,t){const n=ls(e);if(ku(e))return n;if(!dn(e)){let o=Ir(e);for(;o&&!_a(o);){if(Ws(o)&&!Ih(o))return o;o=Ir(o)}return n}let r=oy(e,t);for(;r&>(r)&&Ih(r);)r=oy(r,t);return r&&_a(r)&&Ih(r)&&!vf(r)?n:r||JT(e)||n}const hI=async function(e){const t=this.getOffsetParent||JN,n=this.getDimensions,r=await n(e.floating);return{reference:uI(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}};function mI(e){return Us(e).direction==="rtl"}const pI={convertOffsetParentRelativeRectToViewportRelativeRect:sI,getDocumentElement:hn,getClippingRect:cI,getOffsetParent:JN,getElementRects:hI,getClientRects:nI,getDimensions:dI,getScale:ra,isElement:Ws,isRTL:mI};function ZN(e,t){return e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height}function xI(e,t){let n=null,r;const o=hn(e);function a(){var l;clearTimeout(r),(l=n)==null||l.disconnect(),n=null}function i(l,c){l===void 0&&(l=!1),c===void 0&&(c=1),a();const d=e.getBoundingClientRect(),{left:u,top:h,width:p,height:m}=d;if(l||t(),!p||!m)return;const w=oc(h),x=oc(o.clientWidth-(u+p)),b=oc(o.clientHeight-(h+m)),v=oc(u),y={rootMargin:-w+"px "+-x+"px "+-b+"px "+-v+"px",threshold:rs(0,Tr(1,c))||1};let j=!0;function N(_){const k=_[0].intersectionRatio;if(k!==c){if(!j)return i();k?i(!1,k):r=setTimeout(()=>{i(!1,1e-7)},1e3)}k===1&&!ZN(d,e.getBoundingClientRect())&&i(),j=!1}try{n=new IntersectionObserver(N,{...y,root:o.ownerDocument})}catch{n=new IntersectionObserver(N,y)}n.observe(e)}return i(!0),a}function fI(e,t,n,r){r===void 0&&(r={});const{ancestorScroll:o=!0,ancestorResize:a=!0,elementResize:i=typeof ResizeObserver=="function",layoutShift:l=typeof IntersectionObserver=="function",animationFrame:c=!1}=r,d=bf(e),u=o||a?[...d?dl(d):[],...dl(t)]:[];u.forEach(v=>{o&&v.addEventListener("scroll",n,{passive:!0}),a&&v.addEventListener("resize",n)});const h=d&&l?xI(d,n):null;let p=-1,m=null;i&&(m=new ResizeObserver(v=>{let[g]=v;g&&g.target===d&&m&&(m.unobserve(t),cancelAnimationFrame(p),p=requestAnimationFrame(()=>{var y;(y=m)==null||y.observe(t)})),n()}),d&&!c&&m.observe(d),m.observe(t));let w,x=c?mo(e):null;c&&b();function b(){const v=mo(e);x&&!ZN(x,v)&&n(),x=v,w=requestAnimationFrame(b)}return n(),()=>{var v;u.forEach(g=>{o&&g.removeEventListener("scroll",n),a&&g.removeEventListener("resize",n)}),h==null||h(),(v=m)==null||v.disconnect(),m=null,c&&cancelAnimationFrame(w)}}const gI=BT,vI=WT,yI=LT,bI=HT,wI=FT,ay=$T,jI=UT,NI=(e,t,n)=>{const r=new Map,o={platform:pI,...n},a={...o.platform,_c:r};return MT(e,t,{...o,platform:a})};var kI=typeof document<"u",_I=function(){},$c=kI?f.useLayoutEffect:_I;function Ed(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let n,r,o;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(n=e.length,n!==t.length)return!1;for(r=n;r--!==0;)if(!Ed(e[r],t[r]))return!1;return!0}if(o=Object.keys(e),n=o.length,n!==Object.keys(t).length)return!1;for(r=n;r--!==0;)if(!{}.hasOwnProperty.call(t,o[r]))return!1;for(r=n;r--!==0;){const a=o[r];if(!(a==="_owner"&&e.$$typeof)&&!Ed(e[a],t[a]))return!1}return!0}return e!==e&&t!==t}function e1(e){return typeof window>"u"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function iy(e,t){const n=e1(e);return Math.round(t*n)/n}function Ah(e){const t=f.useRef(e);return $c(()=>{t.current=e}),t}function CI(e){e===void 0&&(e={});const{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:o,elements:{reference:a,floating:i}={},transform:l=!0,whileElementsMounted:c,open:d}=e,[u,h]=f.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[p,m]=f.useState(r);Ed(p,r)||m(r);const[w,x]=f.useState(null),[b,v]=f.useState(null),g=f.useCallback(E=>{E!==_.current&&(_.current=E,x(E))},[]),y=f.useCallback(E=>{E!==k.current&&(k.current=E,v(E))},[]),j=a||w,N=i||b,_=f.useRef(null),k=f.useRef(null),C=f.useRef(u),P=c!=null,A=Ah(c),H=Ah(o),M=Ah(d),K=f.useCallback(()=>{if(!_.current||!k.current)return;const E={placement:t,strategy:n,middleware:p};H.current&&(E.platform=H.current),NI(_.current,k.current,E).then(T=>{const $={...T,isPositioned:M.current!==!1};O.current&&!Ed(C.current,$)&&(C.current=$,Oa.flushSync(()=>{h($)}))})},[p,t,n,H,M]);$c(()=>{d===!1&&C.current.isPositioned&&(C.current.isPositioned=!1,h(E=>({...E,isPositioned:!1})))},[d]);const O=f.useRef(!1);$c(()=>(O.current=!0,()=>{O.current=!1}),[]),$c(()=>{if(j&&(_.current=j),N&&(k.current=N),j&&N){if(A.current)return A.current(j,N,K);K()}},[j,N,K,A,P]);const X=f.useMemo(()=>({reference:_,floating:k,setReference:g,setFloating:y}),[g,y]),q=f.useMemo(()=>({reference:j,floating:N}),[j,N]),Y=f.useMemo(()=>{const E={position:n,left:0,top:0};if(!q.floating)return E;const T=iy(q.floating,u.x),$=iy(q.floating,u.y);return l?{...E,transform:"translate("+T+"px, "+$+"px)",...e1(q.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:T,top:$}},[n,l,q.floating,u.x,u.y]);return f.useMemo(()=>({...u,update:K,refs:X,elements:q,floatingStyles:Y}),[u,K,X,q,Y])}const SI=e=>{function t(n){return{}.hasOwnProperty.call(n,"current")}return{name:"arrow",options:e,fn(n){const{element:r,padding:o}=typeof e=="function"?e(n):e;return r&&t(r)?r.current!=null?ay({element:r.current,padding:o}).fn(n):{}:r?ay({element:r,padding:o}).fn(n):{}}}},EI=(e,t)=>({...gI(e),options:[e,t]}),TI=(e,t)=>({...vI(e),options:[e,t]}),II=(e,t)=>({...jI(e),options:[e,t]}),AI=(e,t)=>({...yI(e),options:[e,t]}),PI=(e,t)=>({...bI(e),options:[e,t]}),OI=(e,t)=>({...wI(e),options:[e,t]}),DI=(e,t)=>({...SI(e),options:[e,t]});var RI="Arrow",t1=f.forwardRef((e,t)=>{const{children:n,width:r=10,height:o=5,...a}=e;return s.jsx(be.svg,{...a,ref:t,width:r,height:o,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?n:s.jsx("polygon",{points:"0,0 30,0 15,10"})})});t1.displayName=RI;var MI=t1;function $I(e){const[t,n]=f.useState(void 0);return vt(()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const r=new ResizeObserver(o=>{if(!Array.isArray(o)||!o.length)return;const a=o[0];let i,l;if("borderBoxSize"in a){const c=a.borderBoxSize,d=Array.isArray(c)?c[0]:c;i=d.inlineSize,l=d.blockSize}else i=e.offsetWidth,l=e.offsetHeight;n({width:i,height:l})});return r.observe(e,{box:"border-box"}),()=>r.unobserve(e)}else n(void 0)},[e]),t}var jf="Popper",[s1,Cu]=Ra(jf),[LI,n1]=s1(jf),r1=e=>{const{__scopePopper:t,children:n}=e,[r,o]=f.useState(null);return s.jsx(LI,{scope:t,anchor:r,onAnchorChange:o,children:n})};r1.displayName=jf;var o1="PopperAnchor",a1=f.forwardRef((e,t)=>{const{__scopePopper:n,virtualRef:r,...o}=e,a=n1(o1,n),i=f.useRef(null),l=We(t,i);return f.useEffect(()=>{a.onAnchorChange((r==null?void 0:r.current)||i.current)}),r?null:s.jsx(be.div,{...o,ref:l})});a1.displayName=o1;var Nf="PopperContent",[FI,zI]=s1(Nf),i1=f.forwardRef((e,t)=>{var F,ke,Ue,ce,xe,fe;const{__scopePopper:n,side:r="bottom",sideOffset:o=0,align:a="center",alignOffset:i=0,arrowPadding:l=0,avoidCollisions:c=!0,collisionBoundary:d=[],collisionPadding:u=0,sticky:h="partial",hideWhenDetached:p=!1,updatePositionStrategy:m="optimized",onPlaced:w,...x}=e,b=n1(Nf,n),[v,g]=f.useState(null),y=We(t,tt=>g(tt)),[j,N]=f.useState(null),_=$I(j),k=(_==null?void 0:_.width)??0,C=(_==null?void 0:_.height)??0,P=r+(a!=="center"?"-"+a:""),A=typeof u=="number"?u:{top:0,right:0,bottom:0,left:0,...u},H=Array.isArray(d)?d:[d],M=H.length>0,K={padding:A,boundary:H.filter(WI),altBoundary:M},{refs:O,floatingStyles:X,placement:q,isPositioned:Y,middlewareData:E}=CI({strategy:"fixed",placement:P,whileElementsMounted:(...tt)=>fI(...tt,{animationFrame:m==="always"}),elements:{reference:b.anchor},middleware:[EI({mainAxis:o+C,alignmentAxis:i}),c&&TI({mainAxis:!0,crossAxis:!1,limiter:h==="partial"?II():void 0,...K}),c&&AI({...K}),PI({...K,apply:({elements:tt,rects:Et,availableWidth:qs,availableHeight:js})=>{const{width:Ns,height:Va}=Et.reference,No=tt.floating.style;No.setProperty("--radix-popper-available-width",`${qs}px`),No.setProperty("--radix-popper-available-height",`${js}px`),No.setProperty("--radix-popper-anchor-width",`${Ns}px`),No.setProperty("--radix-popper-anchor-height",`${Va}px`)}}),j&&DI({element:j,padding:l}),UI({arrowWidth:k,arrowHeight:C}),p&&OI({strategy:"referenceHidden",...K})]}),[T,$]=d1(q),Z=Bs(w);vt(()=>{Y&&(Z==null||Z())},[Y,Z]);const W=(F=E.arrow)==null?void 0:F.x,ee=(ke=E.arrow)==null?void 0:ke.y,J=((Ue=E.arrow)==null?void 0:Ue.centerOffset)!==0,[we,Ne]=f.useState();return vt(()=>{v&&Ne(window.getComputedStyle(v).zIndex)},[v]),s.jsx("div",{ref:O.setFloating,"data-radix-popper-content-wrapper":"",style:{...X,transform:Y?X.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:we,"--radix-popper-transform-origin":[(ce=E.transformOrigin)==null?void 0:ce.x,(xe=E.transformOrigin)==null?void 0:xe.y].join(" "),...((fe=E.hide)==null?void 0:fe.referenceHidden)&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:s.jsx(FI,{scope:n,placedSide:T,onArrowChange:N,arrowX:W,arrowY:ee,shouldHideArrow:J,children:s.jsx(be.div,{"data-side":T,"data-align":$,...x,ref:y,style:{...x.style,animation:Y?void 0:"none"}})})})});i1.displayName=Nf;var l1="PopperArrow",BI={top:"bottom",right:"left",bottom:"top",left:"right"},c1=f.forwardRef(function(t,n){const{__scopePopper:r,...o}=t,a=zI(l1,r),i=BI[a.placedSide];return s.jsx("span",{ref:a.onArrowChange,style:{position:"absolute",left:a.arrowX,top:a.arrowY,[i]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[a.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[a.placedSide],visibility:a.shouldHideArrow?"hidden":void 0},children:s.jsx(MI,{...o,ref:n,style:{...o.style,display:"block"}})})});c1.displayName=l1;function WI(e){return e!==null}var UI=e=>({name:"transformOrigin",options:e,fn(t){var b,v,g;const{placement:n,rects:r,middlewareData:o}=t,i=((b=o.arrow)==null?void 0:b.centerOffset)!==0,l=i?0:e.arrowWidth,c=i?0:e.arrowHeight,[d,u]=d1(n),h={start:"0%",center:"50%",end:"100%"}[u],p=(((v=o.arrow)==null?void 0:v.x)??0)+l/2,m=(((g=o.arrow)==null?void 0:g.y)??0)+c/2;let w="",x="";return d==="bottom"?(w=i?h:`${p}px`,x=`${-c}px`):d==="top"?(w=i?h:`${p}px`,x=`${r.floating.height+c}px`):d==="right"?(w=`${-c}px`,x=i?h:`${m}px`):d==="left"&&(w=`${r.floating.width+c}px`,x=i?h:`${m}px`),{data:{x:w,y:x}}}});function d1(e){const[t,n="center"]=e.split("-");return[t,n]}var HI=r1,u1=a1,h1=i1,m1=c1,[Su,o$]=Ra("Tooltip",[Cu]),kf=Cu(),p1="TooltipProvider",qI=700,ly="tooltip.open",[VI,x1]=Su(p1),f1=e=>{const{__scopeTooltip:t,delayDuration:n=qI,skipDelayDuration:r=300,disableHoverableContent:o=!1,children:a}=e,i=f.useRef(!0),l=f.useRef(!1),c=f.useRef(0);return f.useEffect(()=>{const d=c.current;return()=>window.clearTimeout(d)},[]),s.jsx(VI,{scope:t,isOpenDelayedRef:i,delayDuration:n,onOpen:f.useCallback(()=>{window.clearTimeout(c.current),i.current=!1},[]),onClose:f.useCallback(()=>{window.clearTimeout(c.current),c.current=window.setTimeout(()=>i.current=!0,r)},[r]),isPointerInTransitRef:l,onPointerInTransitChange:f.useCallback(d=>{l.current=d},[]),disableHoverableContent:o,children:a})};f1.displayName=p1;var g1="Tooltip",[a$,Eu]=Su(g1),yp="TooltipTrigger",GI=f.forwardRef((e,t)=>{const{__scopeTooltip:n,...r}=e,o=Eu(yp,n),a=x1(yp,n),i=kf(n),l=f.useRef(null),c=We(t,l,o.onTriggerChange),d=f.useRef(!1),u=f.useRef(!1),h=f.useCallback(()=>d.current=!1,[]);return f.useEffect(()=>()=>document.removeEventListener("pointerup",h),[h]),s.jsx(u1,{asChild:!0,...i,children:s.jsx(be.button,{"aria-describedby":o.open?o.contentId:void 0,"data-state":o.stateAttribute,...r,ref:c,onPointerMove:le(e.onPointerMove,p=>{p.pointerType!=="touch"&&!u.current&&!a.isPointerInTransitRef.current&&(o.onTriggerEnter(),u.current=!0)}),onPointerLeave:le(e.onPointerLeave,()=>{o.onTriggerLeave(),u.current=!1}),onPointerDown:le(e.onPointerDown,()=>{o.open&&o.onClose(),d.current=!0,document.addEventListener("pointerup",h,{once:!0})}),onFocus:le(e.onFocus,()=>{d.current||o.onOpen()}),onBlur:le(e.onBlur,o.onClose),onClick:le(e.onClick,o.onClose)})})});GI.displayName=yp;var YI="TooltipPortal",[i$,QI]=Su(YI,{forceMount:void 0}),Ca="TooltipContent",v1=f.forwardRef((e,t)=>{const n=QI(Ca,e.__scopeTooltip),{forceMount:r=n.forceMount,side:o="top",...a}=e,i=Eu(Ca,e.__scopeTooltip);return s.jsx(Ma,{present:r||i.open,children:i.disableHoverableContent?s.jsx(y1,{side:o,...a,ref:t}):s.jsx(KI,{side:o,...a,ref:t})})}),KI=f.forwardRef((e,t)=>{const n=Eu(Ca,e.__scopeTooltip),r=x1(Ca,e.__scopeTooltip),o=f.useRef(null),a=We(t,o),[i,l]=f.useState(null),{trigger:c,onClose:d}=n,u=o.current,{onPointerInTransitChange:h}=r,p=f.useCallback(()=>{l(null),h(!1)},[h]),m=f.useCallback((w,x)=>{const b=w.currentTarget,v={x:w.clientX,y:w.clientY},g=tA(v,b.getBoundingClientRect()),y=sA(v,g),j=nA(x.getBoundingClientRect()),N=oA([...y,...j]);l(N),h(!0)},[h]);return f.useEffect(()=>()=>p(),[p]),f.useEffect(()=>{if(c&&u){const w=b=>m(b,u),x=b=>m(b,c);return c.addEventListener("pointerleave",w),u.addEventListener("pointerleave",x),()=>{c.removeEventListener("pointerleave",w),u.removeEventListener("pointerleave",x)}}},[c,u,m,p]),f.useEffect(()=>{if(i){const w=x=>{const b=x.target,v={x:x.clientX,y:x.clientY},g=(c==null?void 0:c.contains(b))||(u==null?void 0:u.contains(b)),y=!rA(v,i);g?p():y&&(p(),d())};return document.addEventListener("pointermove",w),()=>document.removeEventListener("pointermove",w)}},[c,u,i,d,p]),s.jsx(y1,{...e,ref:a})}),[XI,JI]=Su(g1,{isInside:!1}),ZI=t4("TooltipContent"),y1=f.forwardRef((e,t)=>{const{__scopeTooltip:n,children:r,"aria-label":o,onEscapeKeyDown:a,onPointerDownOutside:i,...l}=e,c=Eu(Ca,n),d=kf(n),{onClose:u}=c;return f.useEffect(()=>(document.addEventListener(ly,u),()=>document.removeEventListener(ly,u)),[u]),f.useEffect(()=>{if(c.trigger){const h=p=>{const m=p.target;m!=null&&m.contains(c.trigger)&&u()};return window.addEventListener("scroll",h,{capture:!0}),()=>window.removeEventListener("scroll",h,{capture:!0})}},[c.trigger,u]),s.jsx(Tl,{asChild:!0,disableOutsidePointerEvents:!1,onEscapeKeyDown:a,onPointerDownOutside:i,onFocusOutside:h=>h.preventDefault(),onDismiss:u,children:s.jsxs(h1,{"data-state":c.stateAttribute,...d,...l,ref:t,style:{...l.style,"--radix-tooltip-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-tooltip-content-available-width":"var(--radix-popper-available-width)","--radix-tooltip-content-available-height":"var(--radix-popper-available-height)","--radix-tooltip-trigger-width":"var(--radix-popper-anchor-width)","--radix-tooltip-trigger-height":"var(--radix-popper-anchor-height)"},children:[s.jsx(ZI,{children:r}),s.jsx(XI,{scope:n,isInside:!0,children:s.jsx(N4,{id:c.contentId,role:"tooltip",children:o||r})})]})})});v1.displayName=Ca;var b1="TooltipArrow",eA=f.forwardRef((e,t)=>{const{__scopeTooltip:n,...r}=e,o=kf(n);return JI(b1,n).isInside?null:s.jsx(m1,{...o,...r,ref:t})});eA.displayName=b1;function tA(e,t){const n=Math.abs(t.top-e.y),r=Math.abs(t.bottom-e.y),o=Math.abs(t.right-e.x),a=Math.abs(t.left-e.x);switch(Math.min(n,r,o,a)){case a:return"left";case o:return"right";case n:return"top";case r:return"bottom";default:throw new Error("unreachable")}}function sA(e,t,n=5){const r=[];switch(t){case"top":r.push({x:e.x-n,y:e.y+n},{x:e.x+n,y:e.y+n});break;case"bottom":r.push({x:e.x-n,y:e.y-n},{x:e.x+n,y:e.y-n});break;case"left":r.push({x:e.x+n,y:e.y-n},{x:e.x+n,y:e.y+n});break;case"right":r.push({x:e.x-n,y:e.y-n},{x:e.x-n,y:e.y+n});break}return r}function nA(e){const{top:t,right:n,bottom:r,left:o}=e;return[{x:o,y:t},{x:n,y:t},{x:n,y:r},{x:o,y:r}]}function rA(e,t){const{x:n,y:r}=e;let o=!1;for(let a=0,i=t.length-1;ar!=p>r&&n<(h-d)*(r-u)/(p-u)+d&&(o=!o)}return o}function oA(e){const t=e.slice();return t.sort((n,r)=>n.xr.x?1:n.yr.y?1:0),aA(t)}function aA(e){if(e.length<=1)return e.slice();const t=[];for(let r=0;r=2;){const a=t[t.length-1],i=t[t.length-2];if((a.x-i.x)*(o.y-i.y)>=(a.y-i.y)*(o.x-i.x))t.pop();else break}t.push(o)}t.pop();const n=[];for(let r=e.length-1;r>=0;r--){const o=e[r];for(;n.length>=2;){const a=n[n.length-1],i=n[n.length-2];if((a.x-i.x)*(o.y-i.y)>=(a.y-i.y)*(o.x-i.x))n.pop();else break}n.push(o)}return n.pop(),t.length===1&&n.length===1&&t[0].x===n[0].x&&t[0].y===n[0].y?t:t.concat(n)}var iA=f1,w1=v1;const lA=iA,cA=f.forwardRef(({className:e,sideOffset:t=4,...n},r)=>s.jsx(w1,{ref:r,sideOffset:t,className:Me("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n}));cA.displayName=w1.displayName;var Tu=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},Iu=typeof window>"u"||"Deno"in globalThis;function Ps(){}function dA(e,t){return typeof e=="function"?e(t):e}function uA(e){return typeof e=="number"&&e>=0&&e!==1/0}function hA(e,t){return Math.max(e+(t||0)-Date.now(),0)}function bp(e,t){return typeof e=="function"?e(t):e}function mA(e,t){return typeof e=="function"?e(t):e}function cy(e,t){const{type:n="all",exact:r,fetchStatus:o,predicate:a,queryKey:i,stale:l}=e;if(i){if(r){if(t.queryHash!==_f(i,t.options))return!1}else if(!hl(t.queryKey,i))return!1}if(n!=="all"){const c=t.isActive();if(n==="active"&&!c||n==="inactive"&&c)return!1}return!(typeof l=="boolean"&&t.isStale()!==l||o&&o!==t.state.fetchStatus||a&&!a(t))}function dy(e,t){const{exact:n,status:r,predicate:o,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(ul(t.options.mutationKey)!==ul(a))return!1}else if(!hl(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||o&&!o(t))}function _f(e,t){return((t==null?void 0:t.queryKeyHashFn)||ul)(e)}function ul(e){return JSON.stringify(e,(t,n)=>wp(n)?Object.keys(n).sort().reduce((r,o)=>(r[o]=n[o],r),{}):n)}function hl(e,t){return e===t?!0:typeof e!=typeof t?!1:e&&t&&typeof e=="object"&&typeof t=="object"?Object.keys(t).every(n=>hl(e[n],t[n])):!1}function j1(e,t){if(e===t)return e;const n=uy(e)&&uy(t);if(n||wp(e)&&wp(t)){const r=n?e:Object.keys(e),o=r.length,a=n?t:Object.keys(t),i=a.length,l=n?[]:{},c=new Set(r);let d=0;for(let u=0;u{setTimeout(t,e)})}function xA(e,t,n){return typeof n.structuralSharing=="function"?n.structuralSharing(e,t):n.structuralSharing!==!1?j1(e,t):t}function fA(e,t,n=0){const r=[...e,t];return n&&r.length>n?r.slice(1):r}function gA(e,t,n=0){const r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var Cf=Symbol();function N1(e,t){return!e.queryFn&&(t!=null&&t.initialPromise)?()=>t.initialPromise:!e.queryFn||e.queryFn===Cf?()=>Promise.reject(new Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}var Zr,rr,la,kw,vA=(kw=class extends Tu{constructor(){super();je(this,Zr);je(this,rr);je(this,la);oe(this,la,t=>{if(!Iu&&window.addEventListener){const n=()=>t();return window.addEventListener("visibilitychange",n,!1),()=>{window.removeEventListener("visibilitychange",n)}}})}onSubscribe(){I(this,rr)||this.setEventListener(I(this,la))}onUnsubscribe(){var t;this.hasListeners()||((t=I(this,rr))==null||t.call(this),oe(this,rr,void 0))}setEventListener(t){var n;oe(this,la,t),(n=I(this,rr))==null||n.call(this),oe(this,rr,t(r=>{typeof r=="boolean"?this.setFocused(r):this.onFocus()}))}setFocused(t){I(this,Zr)!==t&&(oe(this,Zr,t),this.onFocus())}onFocus(){const t=this.isFocused();this.listeners.forEach(n=>{n(t)})}isFocused(){var t;return typeof I(this,Zr)=="boolean"?I(this,Zr):((t=globalThis.document)==null?void 0:t.visibilityState)!=="hidden"}},Zr=new WeakMap,rr=new WeakMap,la=new WeakMap,kw),k1=new vA,ca,or,da,_w,yA=(_w=class extends Tu{constructor(){super();je(this,ca,!0);je(this,or);je(this,da);oe(this,da,t=>{if(!Iu&&window.addEventListener){const n=()=>t(!0),r=()=>t(!1);return window.addEventListener("online",n,!1),window.addEventListener("offline",r,!1),()=>{window.removeEventListener("online",n),window.removeEventListener("offline",r)}}})}onSubscribe(){I(this,or)||this.setEventListener(I(this,da))}onUnsubscribe(){var t;this.hasListeners()||((t=I(this,or))==null||t.call(this),oe(this,or,void 0))}setEventListener(t){var n;oe(this,da,t),(n=I(this,or))==null||n.call(this),oe(this,or,t(this.setOnline.bind(this)))}setOnline(t){I(this,ca)!==t&&(oe(this,ca,t),this.listeners.forEach(r=>{r(t)}))}isOnline(){return I(this,ca)}},ca=new WeakMap,or=new WeakMap,da=new WeakMap,_w),Td=new yA;function bA(){let e,t;const n=new Promise((o,a)=>{e=o,t=a});n.status="pending",n.catch(()=>{});function r(o){Object.assign(n,o),delete n.resolve,delete n.reject}return n.resolve=o=>{r({status:"fulfilled",value:o}),e(o)},n.reject=o=>{r({status:"rejected",reason:o}),t(o)},n}function wA(e){return Math.min(1e3*2**e,3e4)}function _1(e){return(e??"online")==="online"?Td.isOnline():!0}var C1=class extends Error{constructor(e){super("CancelledError"),this.revert=e==null?void 0:e.revert,this.silent=e==null?void 0:e.silent}};function Ph(e){return e instanceof C1}function S1(e){let t=!1,n=0,r=!1,o;const a=bA(),i=x=>{var b;r||(p(new C1(x)),(b=e.abort)==null||b.call(e))},l=()=>{t=!0},c=()=>{t=!1},d=()=>k1.isFocused()&&(e.networkMode==="always"||Td.isOnline())&&e.canRun(),u=()=>_1(e.networkMode)&&e.canRun(),h=x=>{var b;r||(r=!0,(b=e.onSuccess)==null||b.call(e,x),o==null||o(),a.resolve(x))},p=x=>{var b;r||(r=!0,(b=e.onError)==null||b.call(e,x),o==null||o(),a.reject(x))},m=()=>new Promise(x=>{var b;o=v=>{(r||d())&&x(v)},(b=e.onPause)==null||b.call(e)}).then(()=>{var x;o=void 0,r||(x=e.onContinue)==null||x.call(e)}),w=()=>{if(r)return;let x;const b=n===0?e.initialPromise:void 0;try{x=b??e.fn()}catch(v){x=Promise.reject(v)}Promise.resolve(x).then(h).catch(v=>{var _;if(r)return;const g=e.retry??(Iu?0:3),y=e.retryDelay??wA,j=typeof y=="function"?y(n,v):y,N=g===!0||typeof g=="number"&&nd()?void 0:m()).then(()=>{t?p(v):w()})})};return{promise:a,cancel:i,continue:()=>(o==null||o(),a),cancelRetry:l,continueRetry:c,canStart:u,start:()=>(u()?w():m().then(w),a)}}var jA=e=>setTimeout(e,0);function NA(){let e=[],t=0,n=l=>{l()},r=l=>{l()},o=jA;const a=l=>{t?e.push(l):o(()=>{n(l)})},i=()=>{const l=e;e=[],l.length&&o(()=>{r(()=>{l.forEach(c=>{n(c)})})})};return{batch:l=>{let c;t++;try{c=l()}finally{t--,t||i()}return c},batchCalls:l=>(...c)=>{a(()=>{l(...c)})},schedule:a,setNotifyFunction:l=>{n=l},setBatchNotifyFunction:l=>{r=l},setScheduler:l=>{o=l}}}var Pt=NA(),eo,Cw,E1=(Cw=class{constructor(){je(this,eo)}destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),uA(this.gcTime)&&oe(this,eo,setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(Iu?1/0:5*60*1e3))}clearGcTimeout(){I(this,eo)&&(clearTimeout(I(this,eo)),oe(this,eo,void 0))}},eo=new WeakMap,Cw),ua,to,ms,so,_t,jl,no,Os,fn,Sw,kA=(Sw=class extends E1{constructor(t){super();je(this,Os);je(this,ua);je(this,to);je(this,ms);je(this,so);je(this,_t);je(this,jl);je(this,no);oe(this,no,!1),oe(this,jl,t.defaultOptions),this.setOptions(t.options),this.observers=[],oe(this,so,t.client),oe(this,ms,I(this,so).getQueryCache()),this.queryKey=t.queryKey,this.queryHash=t.queryHash,oe(this,ua,CA(this.options)),this.state=t.state??I(this,ua),this.scheduleGc()}get meta(){return this.options.meta}get promise(){var t;return(t=I(this,_t))==null?void 0:t.promise}setOptions(t){this.options={...I(this,jl),...t},this.updateGcTime(this.options.gcTime)}optionalRemove(){!this.observers.length&&this.state.fetchStatus==="idle"&&I(this,ms).remove(this)}setData(t,n){const r=xA(this.state.data,t,this.options);return wt(this,Os,fn).call(this,{data:r,type:"success",dataUpdatedAt:n==null?void 0:n.updatedAt,manual:n==null?void 0:n.manual}),r}setState(t,n){wt(this,Os,fn).call(this,{type:"setState",state:t,setStateOptions:n})}cancel(t){var r,o;const n=(r=I(this,_t))==null?void 0:r.promise;return(o=I(this,_t))==null||o.cancel(t),n?n.then(Ps).catch(Ps):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}reset(){this.destroy(),this.setState(I(this,ua))}isActive(){return this.observers.some(t=>mA(t.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===Cf||this.state.dataUpdateCount+this.state.errorUpdateCount===0}isStatic(){return this.getObserversCount()>0?this.observers.some(t=>bp(t.options.staleTime,this)==="static"):!1}isStale(){return this.getObserversCount()>0?this.observers.some(t=>t.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(t=0){return this.state.data===void 0?!0:t==="static"?!1:this.state.isInvalidated?!0:!hA(this.state.dataUpdatedAt,t)}onFocus(){var n;const t=this.observers.find(r=>r.shouldFetchOnWindowFocus());t==null||t.refetch({cancelRefetch:!1}),(n=I(this,_t))==null||n.continue()}onOnline(){var n;const t=this.observers.find(r=>r.shouldFetchOnReconnect());t==null||t.refetch({cancelRefetch:!1}),(n=I(this,_t))==null||n.continue()}addObserver(t){this.observers.includes(t)||(this.observers.push(t),this.clearGcTimeout(),I(this,ms).notify({type:"observerAdded",query:this,observer:t}))}removeObserver(t){this.observers.includes(t)&&(this.observers=this.observers.filter(n=>n!==t),this.observers.length||(I(this,_t)&&(I(this,no)?I(this,_t).cancel({revert:!0}):I(this,_t).cancelRetry()),this.scheduleGc()),I(this,ms).notify({type:"observerRemoved",query:this,observer:t}))}getObserversCount(){return this.observers.length}invalidate(){this.state.isInvalidated||wt(this,Os,fn).call(this,{type:"invalidate"})}fetch(t,n){var d,u,h;if(this.state.fetchStatus!=="idle"){if(this.state.data!==void 0&&(n!=null&&n.cancelRefetch))this.cancel({silent:!0});else if(I(this,_t))return I(this,_t).continueRetry(),I(this,_t).promise}if(t&&this.setOptions(t),!this.options.queryFn){const p=this.observers.find(m=>m.options.queryFn);p&&this.setOptions(p.options)}const r=new AbortController,o=p=>{Object.defineProperty(p,"signal",{enumerable:!0,get:()=>(oe(this,no,!0),r.signal)})},a=()=>{const p=N1(this.options,n),w=(()=>{const x={client:I(this,so),queryKey:this.queryKey,meta:this.meta};return o(x),x})();return oe(this,no,!1),this.options.persister?this.options.persister(p,w,this):p(w)},l=(()=>{const p={fetchOptions:n,options:this.options,queryKey:this.queryKey,client:I(this,so),state:this.state,fetchFn:a};return o(p),p})();(d=this.options.behavior)==null||d.onFetch(l,this),oe(this,to,this.state),(this.state.fetchStatus==="idle"||this.state.fetchMeta!==((u=l.fetchOptions)==null?void 0:u.meta))&&wt(this,Os,fn).call(this,{type:"fetch",meta:(h=l.fetchOptions)==null?void 0:h.meta});const c=p=>{var m,w,x,b;Ph(p)&&p.silent||wt(this,Os,fn).call(this,{type:"error",error:p}),Ph(p)||((w=(m=I(this,ms).config).onError)==null||w.call(m,p,this),(b=(x=I(this,ms).config).onSettled)==null||b.call(x,this.state.data,p,this)),this.scheduleGc()};return oe(this,_t,S1({initialPromise:n==null?void 0:n.initialPromise,fn:l.fetchFn,abort:r.abort.bind(r),onSuccess:p=>{var m,w,x,b;if(p===void 0){c(new Error(`${this.queryHash} data is undefined`));return}try{this.setData(p)}catch(v){c(v);return}(w=(m=I(this,ms).config).onSuccess)==null||w.call(m,p,this),(b=(x=I(this,ms).config).onSettled)==null||b.call(x,p,this.state.error,this),this.scheduleGc()},onError:c,onFail:(p,m)=>{wt(this,Os,fn).call(this,{type:"failed",failureCount:p,error:m})},onPause:()=>{wt(this,Os,fn).call(this,{type:"pause"})},onContinue:()=>{wt(this,Os,fn).call(this,{type:"continue"})},retry:l.options.retry,retryDelay:l.options.retryDelay,networkMode:l.options.networkMode,canRun:()=>!0})),I(this,_t).start()}},ua=new WeakMap,to=new WeakMap,ms=new WeakMap,so=new WeakMap,_t=new WeakMap,jl=new WeakMap,no=new WeakMap,Os=new WeakSet,fn=function(t){const n=r=>{switch(t.type){case"failed":return{...r,fetchFailureCount:t.failureCount,fetchFailureReason:t.error};case"pause":return{...r,fetchStatus:"paused"};case"continue":return{...r,fetchStatus:"fetching"};case"fetch":return{...r,..._A(r.data,this.options),fetchMeta:t.meta??null};case"success":return oe(this,to,void 0),{...r,data:t.data,dataUpdateCount:r.dataUpdateCount+1,dataUpdatedAt:t.dataUpdatedAt??Date.now(),error:null,isInvalidated:!1,status:"success",...!t.manual&&{fetchStatus:"idle",fetchFailureCount:0,fetchFailureReason:null}};case"error":const o=t.error;return Ph(o)&&o.revert&&I(this,to)?{...I(this,to),fetchStatus:"idle"}:{...r,error:o,errorUpdateCount:r.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:r.fetchFailureCount+1,fetchFailureReason:o,fetchStatus:"idle",status:"error"};case"invalidate":return{...r,isInvalidated:!0};case"setState":return{...r,...t.state}}};this.state=n(this.state),Pt.batch(()=>{this.observers.forEach(r=>{r.onQueryUpdate()}),I(this,ms).notify({query:this,type:"updated",action:t})})},Sw);function _A(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:_1(t.networkMode)?"fetching":"paused",...e===void 0&&{error:null,status:"pending"}}}function CA(e){const t=typeof e.initialData=="function"?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt=="function"?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?"success":"pending",fetchStatus:"idle"}}var Xs,Ew,SA=(Ew=class extends Tu{constructor(t={}){super();je(this,Xs);this.config=t,oe(this,Xs,new Map)}build(t,n,r){const o=n.queryKey,a=n.queryHash??_f(o,n);let i=this.get(a);return i||(i=new kA({client:t,queryKey:o,queryHash:a,options:t.defaultQueryOptions(n),state:r,defaultOptions:t.getQueryDefaults(o)}),this.add(i)),i}add(t){I(this,Xs).has(t.queryHash)||(I(this,Xs).set(t.queryHash,t),this.notify({type:"added",query:t}))}remove(t){const n=I(this,Xs).get(t.queryHash);n&&(t.destroy(),n===t&&I(this,Xs).delete(t.queryHash),this.notify({type:"removed",query:t}))}clear(){Pt.batch(()=>{this.getAll().forEach(t=>{this.remove(t)})})}get(t){return I(this,Xs).get(t)}getAll(){return[...I(this,Xs).values()]}find(t){const n={exact:!0,...t};return this.getAll().find(r=>cy(n,r))}findAll(t={}){const n=this.getAll();return Object.keys(t).length>0?n.filter(r=>cy(t,r)):n}notify(t){Pt.batch(()=>{this.listeners.forEach(n=>{n(t)})})}onFocus(){Pt.batch(()=>{this.getAll().forEach(t=>{t.onFocus()})})}onOnline(){Pt.batch(()=>{this.getAll().forEach(t=>{t.onOnline()})})}},Xs=new WeakMap,Ew),Js,It,ro,Zs,Qn,Tw,EA=(Tw=class extends E1{constructor(t){super();je(this,Zs);je(this,Js);je(this,It);je(this,ro);this.mutationId=t.mutationId,oe(this,It,t.mutationCache),oe(this,Js,[]),this.state=t.state||TA(),this.setOptions(t.options),this.scheduleGc()}setOptions(t){this.options=t,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(t){I(this,Js).includes(t)||(I(this,Js).push(t),this.clearGcTimeout(),I(this,It).notify({type:"observerAdded",mutation:this,observer:t}))}removeObserver(t){oe(this,Js,I(this,Js).filter(n=>n!==t)),this.scheduleGc(),I(this,It).notify({type:"observerRemoved",mutation:this,observer:t})}optionalRemove(){I(this,Js).length||(this.state.status==="pending"?this.scheduleGc():I(this,It).remove(this))}continue(){var t;return((t=I(this,ro))==null?void 0:t.continue())??this.execute(this.state.variables)}async execute(t){var a,i,l,c,d,u,h,p,m,w,x,b,v,g,y,j,N,_,k,C;const n=()=>{wt(this,Zs,Qn).call(this,{type:"continue"})};oe(this,ro,S1({fn:()=>this.options.mutationFn?this.options.mutationFn(t):Promise.reject(new Error("No mutationFn found")),onFail:(P,A)=>{wt(this,Zs,Qn).call(this,{type:"failed",failureCount:P,error:A})},onPause:()=>{wt(this,Zs,Qn).call(this,{type:"pause"})},onContinue:n,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>I(this,It).canRun(this)}));const r=this.state.status==="pending",o=!I(this,ro).canStart();try{if(r)n();else{wt(this,Zs,Qn).call(this,{type:"pending",variables:t,isPaused:o}),await((i=(a=I(this,It).config).onMutate)==null?void 0:i.call(a,t,this));const A=await((c=(l=this.options).onMutate)==null?void 0:c.call(l,t));A!==this.state.context&&wt(this,Zs,Qn).call(this,{type:"pending",context:A,variables:t,isPaused:o})}const P=await I(this,ro).start();return await((u=(d=I(this,It).config).onSuccess)==null?void 0:u.call(d,P,t,this.state.context,this)),await((p=(h=this.options).onSuccess)==null?void 0:p.call(h,P,t,this.state.context)),await((w=(m=I(this,It).config).onSettled)==null?void 0:w.call(m,P,null,this.state.variables,this.state.context,this)),await((b=(x=this.options).onSettled)==null?void 0:b.call(x,P,null,t,this.state.context)),wt(this,Zs,Qn).call(this,{type:"success",data:P}),P}catch(P){try{throw await((g=(v=I(this,It).config).onError)==null?void 0:g.call(v,P,t,this.state.context,this)),await((j=(y=this.options).onError)==null?void 0:j.call(y,P,t,this.state.context)),await((_=(N=I(this,It).config).onSettled)==null?void 0:_.call(N,void 0,P,this.state.variables,this.state.context,this)),await((C=(k=this.options).onSettled)==null?void 0:C.call(k,void 0,P,t,this.state.context)),P}finally{wt(this,Zs,Qn).call(this,{type:"error",error:P})}}finally{I(this,It).runNext(this)}}},Js=new WeakMap,It=new WeakMap,ro=new WeakMap,Zs=new WeakSet,Qn=function(t){const n=r=>{switch(t.type){case"failed":return{...r,failureCount:t.failureCount,failureReason:t.error};case"pause":return{...r,isPaused:!0};case"continue":return{...r,isPaused:!1};case"pending":return{...r,context:t.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:t.isPaused,status:"pending",variables:t.variables,submittedAt:Date.now()};case"success":return{...r,data:t.data,failureCount:0,failureReason:null,error:null,status:"success",isPaused:!1};case"error":return{...r,data:void 0,error:t.error,failureCount:r.failureCount+1,failureReason:t.error,isPaused:!1,status:"error"}}};this.state=n(this.state),Pt.batch(()=>{I(this,Js).forEach(r=>{r.onMutationUpdate(t)}),I(this,It).notify({mutation:this,type:"updated",action:t})})},Tw);function TA(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:"idle",variables:void 0,submittedAt:0}}var jn,Ds,Nl,Iw,IA=(Iw=class extends Tu{constructor(t={}){super();je(this,jn);je(this,Ds);je(this,Nl);this.config=t,oe(this,jn,new Set),oe(this,Ds,new Map),oe(this,Nl,0)}build(t,n,r){const o=new EA({mutationCache:this,mutationId:++Ll(this,Nl)._,options:t.defaultMutationOptions(n),state:r});return this.add(o),o}add(t){I(this,jn).add(t);const n=ac(t);if(typeof n=="string"){const r=I(this,Ds).get(n);r?r.push(t):I(this,Ds).set(n,[t])}this.notify({type:"added",mutation:t})}remove(t){if(I(this,jn).delete(t)){const n=ac(t);if(typeof n=="string"){const r=I(this,Ds).get(n);if(r)if(r.length>1){const o=r.indexOf(t);o!==-1&&r.splice(o,1)}else r[0]===t&&I(this,Ds).delete(n)}}this.notify({type:"removed",mutation:t})}canRun(t){const n=ac(t);if(typeof n=="string"){const r=I(this,Ds).get(n),o=r==null?void 0:r.find(a=>a.state.status==="pending");return!o||o===t}else return!0}runNext(t){var r;const n=ac(t);if(typeof n=="string"){const o=(r=I(this,Ds).get(n))==null?void 0:r.find(a=>a!==t&&a.state.isPaused);return(o==null?void 0:o.continue())??Promise.resolve()}else return Promise.resolve()}clear(){Pt.batch(()=>{I(this,jn).forEach(t=>{this.notify({type:"removed",mutation:t})}),I(this,jn).clear(),I(this,Ds).clear()})}getAll(){return Array.from(I(this,jn))}find(t){const n={exact:!0,...t};return this.getAll().find(r=>dy(n,r))}findAll(t={}){return this.getAll().filter(n=>dy(t,n))}notify(t){Pt.batch(()=>{this.listeners.forEach(n=>{n(t)})})}resumePausedMutations(){const t=this.getAll().filter(n=>n.state.isPaused);return Pt.batch(()=>Promise.all(t.map(n=>n.continue().catch(Ps))))}},jn=new WeakMap,Ds=new WeakMap,Nl=new WeakMap,Iw);function ac(e){var t;return(t=e.options.scope)==null?void 0:t.id}function my(e){return{onFetch:(t,n)=>{var u,h,p,m,w;const r=t.options,o=(p=(h=(u=t.fetchOptions)==null?void 0:u.meta)==null?void 0:h.fetchMore)==null?void 0:p.direction,a=((m=t.state.data)==null?void 0:m.pages)||[],i=((w=t.state.data)==null?void 0:w.pageParams)||[];let l={pages:[],pageParams:[]},c=0;const d=async()=>{let x=!1;const b=y=>{Object.defineProperty(y,"signal",{enumerable:!0,get:()=>(t.signal.aborted?x=!0:t.signal.addEventListener("abort",()=>{x=!0}),t.signal)})},v=N1(t.options,t.fetchOptions),g=async(y,j,N)=>{if(x)return Promise.reject();if(j==null&&y.pages.length)return Promise.resolve(y);const k=(()=>{const H={client:t.client,queryKey:t.queryKey,pageParam:j,direction:N?"backward":"forward",meta:t.options.meta};return b(H),H})(),C=await v(k),{maxPages:P}=t.options,A=N?gA:fA;return{pages:A(y.pages,C,P),pageParams:A(y.pageParams,j,P)}};if(o&&a.length){const y=o==="backward",j=y?AA:py,N={pages:a,pageParams:i},_=j(r,N);l=await g(N,_,y)}else{const y=e??a.length;do{const j=c===0?i[0]??r.initialPageParam:py(r,l);if(c>0&&j==null)break;l=await g(l,j),c++}while(c{var x,b;return(b=(x=t.options).persister)==null?void 0:b.call(x,d,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n)}:t.fetchFn=d}}}function py(e,{pages:t,pageParams:n}){const r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function AA(e,{pages:t,pageParams:n}){var r;return t.length>0?(r=e.getPreviousPageParam)==null?void 0:r.call(e,t[0],t,n[0],n):void 0}var Ke,ar,ir,ha,ma,lr,pa,xa,Aw,PA=(Aw=class{constructor(e={}){je(this,Ke);je(this,ar);je(this,ir);je(this,ha);je(this,ma);je(this,lr);je(this,pa);je(this,xa);oe(this,Ke,e.queryCache||new SA),oe(this,ar,e.mutationCache||new IA),oe(this,ir,e.defaultOptions||{}),oe(this,ha,new Map),oe(this,ma,new Map),oe(this,lr,0)}mount(){Ll(this,lr)._++,I(this,lr)===1&&(oe(this,pa,k1.subscribe(async e=>{e&&(await this.resumePausedMutations(),I(this,Ke).onFocus())})),oe(this,xa,Td.subscribe(async e=>{e&&(await this.resumePausedMutations(),I(this,Ke).onOnline())})))}unmount(){var e,t;Ll(this,lr)._--,I(this,lr)===0&&((e=I(this,pa))==null||e.call(this),oe(this,pa,void 0),(t=I(this,xa))==null||t.call(this),oe(this,xa,void 0))}isFetching(e){return I(this,Ke).findAll({...e,fetchStatus:"fetching"}).length}isMutating(e){return I(this,ar).findAll({...e,status:"pending"}).length}getQueryData(e){var n;const t=this.defaultQueryOptions({queryKey:e});return(n=I(this,Ke).get(t.queryHash))==null?void 0:n.state.data}ensureQueryData(e){const t=this.defaultQueryOptions(e),n=I(this,Ke).build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(bp(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return I(this,Ke).findAll(e).map(({queryKey:t,state:n})=>{const r=n.data;return[t,r]})}setQueryData(e,t,n){const r=this.defaultQueryOptions({queryKey:e}),o=I(this,Ke).get(r.queryHash),a=o==null?void 0:o.state.data,i=dA(t,a);if(i!==void 0)return I(this,Ke).build(this,r).setData(i,{...n,manual:!0})}setQueriesData(e,t,n){return Pt.batch(()=>I(this,Ke).findAll(e).map(({queryKey:r})=>[r,this.setQueryData(r,t,n)]))}getQueryState(e){var n;const t=this.defaultQueryOptions({queryKey:e});return(n=I(this,Ke).get(t.queryHash))==null?void 0:n.state}removeQueries(e){const t=I(this,Ke);Pt.batch(()=>{t.findAll(e).forEach(n=>{t.remove(n)})})}resetQueries(e,t){const n=I(this,Ke);return Pt.batch(()=>(n.findAll(e).forEach(r=>{r.reset()}),this.refetchQueries({type:"active",...e},t)))}cancelQueries(e,t={}){const n={revert:!0,...t},r=Pt.batch(()=>I(this,Ke).findAll(e).map(o=>o.cancel(n)));return Promise.all(r).then(Ps).catch(Ps)}invalidateQueries(e,t={}){return Pt.batch(()=>(I(this,Ke).findAll(e).forEach(n=>{n.invalidate()}),(e==null?void 0:e.refetchType)==="none"?Promise.resolve():this.refetchQueries({...e,type:(e==null?void 0:e.refetchType)??(e==null?void 0:e.type)??"active"},t)))}refetchQueries(e,t={}){const n={...t,cancelRefetch:t.cancelRefetch??!0},r=Pt.batch(()=>I(this,Ke).findAll(e).filter(o=>!o.isDisabled()&&!o.isStatic()).map(o=>{let a=o.fetch(void 0,n);return n.throwOnError||(a=a.catch(Ps)),o.state.fetchStatus==="paused"?Promise.resolve():a}));return Promise.all(r).then(Ps)}fetchQuery(e){const t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);const n=I(this,Ke).build(this,t);return n.isStaleByTime(bp(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(Ps).catch(Ps)}fetchInfiniteQuery(e){return e.behavior=my(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(Ps).catch(Ps)}ensureInfiniteQueryData(e){return e.behavior=my(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return Td.isOnline()?I(this,ar).resumePausedMutations():Promise.resolve()}getQueryCache(){return I(this,Ke)}getMutationCache(){return I(this,ar)}getDefaultOptions(){return I(this,ir)}setDefaultOptions(e){oe(this,ir,e)}setQueryDefaults(e,t){I(this,ha).set(ul(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){const t=[...I(this,ha).values()],n={};return t.forEach(r=>{hl(e,r.queryKey)&&Object.assign(n,r.defaultOptions)}),n}setMutationDefaults(e,t){I(this,ma).set(ul(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){const t=[...I(this,ma).values()],n={};return t.forEach(r=>{hl(e,r.mutationKey)&&Object.assign(n,r.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;const t={...I(this,ir).queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||(t.queryHash=_f(t.queryKey,t)),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!=="always"),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode="offlineFirst"),t.queryFn===Cf&&(t.enabled=!1),t}defaultMutationOptions(e){return e!=null&&e._defaulted?e:{...I(this,ir).mutations,...(e==null?void 0:e.mutationKey)&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){I(this,Ke).clear(),I(this,ar).clear()}},Ke=new WeakMap,ar=new WeakMap,ir=new WeakMap,ha=new WeakMap,ma=new WeakMap,lr=new WeakMap,pa=new WeakMap,xa=new WeakMap,Aw),OA=f.createContext(void 0),DA=({client:e,children:t})=>(f.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),s.jsx(OA.Provider,{value:e,children:t}));const RA=new PA,MA=({children:e})=>{const[t,n]=f.useState(!1);return f.useEffect(()=>{n(!0)},[]),s.jsx(DA,{client:RA,children:s.jsxs(lA,{children:[t?s.jsxs(s.Fragment,{children:[s.jsx(H6,{}),s.jsx(jT,{})]}):null,e]})})},$A=()=>{const{pathname:e}=jo();return f.useEffect(()=>{window.scrollTo(0,0)},[e]),null},LA=mf("inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"border border-white/20 bg-transparent text-white hover:bg-white/10",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{medium:"h-10 px-4 py-2 text-sm rounded-md [&_svg]:size-4",small:"h-8 px-3 py-1.5 text-xs rounded-[6px] [&_svg]:size-3.5",lg:"h-11 px-8 text-sm rounded-md [&_svg]:size-4",icon:"h-10 w-10 rounded-md [&_svg]:size-4"}},defaultVariants:{variant:"default",size:"medium"}}),ut=f.forwardRef(({className:e,variant:t,size:n,asChild:r=!1,...o},a)=>{const i=r?Z3:"button";return s.jsx(i,{className:Me(LA({variant:t,size:n,className:e})),ref:a,...o})});ut.displayName="Button";const FA="/assets/cookie-banner-hva4grfH.webp",zA=e=>{const t=window.encodeURIComponent(JSON.stringify(e)),r=400*24*3600*1e3,o=new Date(Date.now()+r);document.cookie=`octomind-consent=${t};sameSite=strict;Domain=.octomind.dev;path=/;expires=${o.toUTCString()}`},BA="localStorage+cookie",WA=()=>{const[e,t]=f.useState(null);f.useEffect(()=>{const r=localStorage.getItem("cookie-consent");t(!r)},[]);const n=r=>{var a;const o=r?BA:"memory";zA({consentDate:new Date,allowOptional:r}),typeof window<"u"&&((a=window.posthog)!=null&&a.set_config)&&window.posthog.set_config({persistence:o}),localStorage.setItem("cookie-consent",r?"all":"essential"),t(!1)};return e!==!0?null:s.jsxs("div",{className:"fixed bottom-0 left-0 z-50 p-4 md:p-6",children:[s.jsx("img",{src:FA,alt:"Octopus holding a cookie",className:"hidden md:block w-56 h-auto absolute bottom-0 left-4 z-10"}),s.jsx("div",{className:"md:ml-60 md:mb-10",children:s.jsxs("div",{className:"rounded-xl p-5 max-w-sm shadow-xl",style:{background:"linear-gradient(90deg, #00AF93 0%, #7271E0 100%)"},children:[s.jsxs("p",{className:"text-white text-sm font-normal leading-snug mb-6",children:["We are using cookies for secure log-in and to improve our app. First party only, no data krakens. Learn more in our"," ",s.jsx(Fs,{to:"/privacy",className:"text-white underline hover:text-white/80 transition-colors",children:"privacy policy"}),"."]}),s.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[s.jsx(ut,{size:"small",variant:"secondary",onClick:()=>n(!1),className:"flex-1",children:"allow essential cookies"}),s.jsx(ut,{size:"small",onClick:()=>n(!0),className:"flex-1 bg-white text-text-inverted hover:bg-white/90 transition-all",children:"allow all cookies"})]})]})})]})};var Oh="focusScope.autoFocusOnMount",Dh="focusScope.autoFocusOnUnmount",xy={bubbles:!1,cancelable:!0},UA="FocusScope",Sf=f.forwardRef((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:a,...i}=e,[l,c]=f.useState(null),d=Bs(o),u=Bs(a),h=f.useRef(null),p=We(t,x=>c(x)),m=f.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;f.useEffect(()=>{if(r){let x=function(y){if(m.paused||!l)return;const j=y.target;l.contains(j)?h.current=j:Kn(h.current,{select:!0})},b=function(y){if(m.paused||!l)return;const j=y.relatedTarget;j!==null&&(l.contains(j)||Kn(h.current,{select:!0}))},v=function(y){if(document.activeElement===document.body)for(const N of y)N.removedNodes.length>0&&Kn(l)};document.addEventListener("focusin",x),document.addEventListener("focusout",b);const g=new MutationObserver(v);return l&&g.observe(l,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",x),document.removeEventListener("focusout",b),g.disconnect()}}},[r,l,m.paused]),f.useEffect(()=>{if(l){gy.add(m);const x=document.activeElement;if(!l.contains(x)){const v=new CustomEvent(Oh,xy);l.addEventListener(Oh,d),l.dispatchEvent(v),v.defaultPrevented||(HA(QA(T1(l)),{select:!0}),document.activeElement===x&&Kn(l))}return()=>{l.removeEventListener(Oh,d),setTimeout(()=>{const v=new CustomEvent(Dh,xy);l.addEventListener(Dh,u),l.dispatchEvent(v),v.defaultPrevented||Kn(x??document.body,{select:!0}),l.removeEventListener(Dh,u),gy.remove(m)},0)}}},[l,d,u,m]);const w=f.useCallback(x=>{if(!n&&!r||m.paused)return;const b=x.key==="Tab"&&!x.altKey&&!x.ctrlKey&&!x.metaKey,v=document.activeElement;if(b&&v){const g=x.currentTarget,[y,j]=qA(g);y&&j?!x.shiftKey&&v===j?(x.preventDefault(),n&&Kn(y,{select:!0})):x.shiftKey&&v===y&&(x.preventDefault(),n&&Kn(j,{select:!0})):v===g&&x.preventDefault()}},[n,r,m.paused]);return s.jsx(be.div,{tabIndex:-1,...i,ref:p,onKeyDown:w})});Sf.displayName=UA;function HA(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(Kn(r,{select:t}),document.activeElement!==n)return}function qA(e){const t=T1(e),n=fy(t,e),r=fy(t.reverse(),e);return[n,r]}function T1(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function fy(e,t){for(const n of e)if(!VA(n,{upTo:t}))return n}function VA(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function GA(e){return e instanceof HTMLInputElement&&"select"in e}function Kn(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&GA(e)&&t&&e.select()}}var gy=YA();function YA(){let e=[];return{add(t){const n=e[0];t!==n&&(n==null||n.pause()),e=vy(e,t),e.unshift(t)},remove(t){var n;e=vy(e,t),(n=e[0])==null||n.resume()}}}function vy(e,t){const n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}function QA(e){return e.filter(t=>t.tagName!=="A")}var Rh=0;function I1(){f.useEffect(()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??yy()),document.body.insertAdjacentElement("beforeend",e[1]??yy()),Rh++,()=>{Rh===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(t=>t.remove()),Rh--}},[])}function yy(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var tn=function(){return tn=Object.assign||function(t){for(var n,r=1,o=arguments.length;r"u")return hP;var t=mP(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},xP=D1(),oa="data-scroll-locked",fP=function(e,t,n,r){var o=e.left,a=e.top,i=e.right,l=e.gap;return n===void 0&&(n="margin"),` .`.concat(XA,` { overflow: hidden `).concat(r,`; padding-right: `).concat(l,"px ").concat(r,`; } body[`).concat(oa,`] { overflow: hidden `).concat(r,`; overscroll-behavior: contain; `).concat([t&&"position: relative ".concat(r,";"),n==="margin"&&` padding-left: `.concat(o,`px; padding-top: `).concat(a,`px; padding-right: `).concat(i,`px; margin-left:0; margin-top:0; margin-right: `).concat(l,"px ").concat(r,`; `),n==="padding"&&"padding-right: ".concat(l,"px ").concat(r,";")].filter(Boolean).join(""),` } .`).concat(Lc,` { right: `).concat(l,"px ").concat(r,`; } .`).concat(Fc,` { margin-right: `).concat(l,"px ").concat(r,`; } .`).concat(Lc," .").concat(Lc,` { right: 0 `).concat(r,`; } .`).concat(Fc," .").concat(Fc,` { margin-right: 0 `).concat(r,`; } body[`).concat(oa,`] { `).concat(JA,": ").concat(l,`px; } `)},wy=function(){var e=parseInt(document.body.getAttribute(oa)||"0",10);return isFinite(e)?e:0},gP=function(){f.useEffect(function(){return document.body.setAttribute(oa,(wy()+1).toString()),function(){var e=wy()-1;e<=0?document.body.removeAttribute(oa):document.body.setAttribute(oa,e.toString())}},[])},vP=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,o=r===void 0?"margin":r;gP();var a=f.useMemo(function(){return pP(o)},[o]);return f.createElement(xP,{styles:fP(a,!t,o,n?"":"!important")})},jp=!1;if(typeof window<"u")try{var ic=Object.defineProperty({},"passive",{get:function(){return jp=!0,!0}});window.addEventListener("test",ic,ic),window.removeEventListener("test",ic,ic)}catch{jp=!1}var Eo=jp?{passive:!1}:!1,yP=function(e){return e.tagName==="TEXTAREA"},R1=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!=="hidden"&&!(n.overflowY===n.overflowX&&!yP(e)&&n[t]==="visible")},bP=function(e){return R1(e,"overflowY")},wP=function(e){return R1(e,"overflowX")},jy=function(e,t){var n=t.ownerDocument,r=t;do{typeof ShadowRoot<"u"&&r instanceof ShadowRoot&&(r=r.host);var o=M1(e,r);if(o){var a=$1(e,r),i=a[1],l=a[2];if(i>l)return!0}r=r.parentNode}while(r&&r!==n.body);return!1},jP=function(e){var t=e.scrollTop,n=e.scrollHeight,r=e.clientHeight;return[t,n,r]},NP=function(e){var t=e.scrollLeft,n=e.scrollWidth,r=e.clientWidth;return[t,n,r]},M1=function(e,t){return e==="v"?bP(t):wP(t)},$1=function(e,t){return e==="v"?jP(t):NP(t)},kP=function(e,t){return e==="h"&&t==="rtl"?-1:1},_P=function(e,t,n,r,o){var a=kP(e,window.getComputedStyle(t).direction),i=a*r,l=n.target,c=t.contains(l),d=!1,u=i>0,h=0,p=0;do{if(!l)break;var m=$1(e,l),w=m[0],x=m[1],b=m[2],v=x-b-a*w;(w||v)&&M1(e,l)&&(h+=v,p+=w);var g=l.parentNode;l=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&l!==document.body||c&&(t.contains(l)||t===l));return(u&&(Math.abs(h)<1||!o)||!u&&(Math.abs(p)<1||!o))&&(d=!0),d},lc=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Ny=function(e){return[e.deltaX,e.deltaY]},ky=function(e){return e&&"current"in e?e.current:e},CP=function(e,t){return e[0]===t[0]&&e[1]===t[1]},SP=function(e){return` .block-interactivity-`.concat(e,` {pointer-events: none;} .allow-interactivity-`).concat(e,` {pointer-events: all;} `)},EP=0,To=[];function TP(e){var t=f.useRef([]),n=f.useRef([0,0]),r=f.useRef(),o=f.useState(EP++)[0],a=f.useState(D1)[0],i=f.useRef(e);f.useEffect(function(){i.current=e},[e]),f.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var x=KA([e.lockRef.current],(e.shards||[]).map(ky),!0).filter(Boolean);return x.forEach(function(b){return b.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),x.forEach(function(b){return b.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var l=f.useCallback(function(x,b){if("touches"in x&&x.touches.length===2||x.type==="wheel"&&x.ctrlKey)return!i.current.allowPinchZoom;var v=lc(x),g=n.current,y="deltaX"in x?x.deltaX:g[0]-v[0],j="deltaY"in x?x.deltaY:g[1]-v[1],N,_=x.target,k=Math.abs(y)>Math.abs(j)?"h":"v";if("touches"in x&&k==="h"&&_.type==="range")return!1;var C=jy(k,_);if(!C)return!0;if(C?N=k:(N=k==="v"?"h":"v",C=jy(k,_)),!C)return!1;if(!r.current&&"changedTouches"in x&&(y||j)&&(r.current=N),!N)return!0;var P=r.current||N;return _P(P,b,x,P==="h"?y:j,!0)},[]),c=f.useCallback(function(x){var b=x;if(!(!To.length||To[To.length-1]!==a)){var v="deltaY"in b?Ny(b):lc(b),g=t.current.filter(function(N){return N.name===b.type&&(N.target===b.target||b.target===N.shadowParent)&&CP(N.delta,v)})[0];if(g&&g.should){b.cancelable&&b.preventDefault();return}if(!g){var y=(i.current.shards||[]).map(ky).filter(Boolean).filter(function(N){return N.contains(b.target)}),j=y.length>0?l(b,y[0]):!i.current.noIsolation;j&&b.cancelable&&b.preventDefault()}}},[]),d=f.useCallback(function(x,b,v,g){var y={name:x,delta:b,target:v,should:g,shadowParent:IP(v)};t.current.push(y),setTimeout(function(){t.current=t.current.filter(function(j){return j!==y})},1)},[]),u=f.useCallback(function(x){n.current=lc(x),r.current=void 0},[]),h=f.useCallback(function(x){d(x.type,Ny(x),x.target,l(x,e.lockRef.current))},[]),p=f.useCallback(function(x){d(x.type,lc(x),x.target,l(x,e.lockRef.current))},[]);f.useEffect(function(){return To.push(a),e.setCallbacks({onScrollCapture:h,onWheelCapture:h,onTouchMoveCapture:p}),document.addEventListener("wheel",c,Eo),document.addEventListener("touchmove",c,Eo),document.addEventListener("touchstart",u,Eo),function(){To=To.filter(function(x){return x!==a}),document.removeEventListener("wheel",c,Eo),document.removeEventListener("touchmove",c,Eo),document.removeEventListener("touchstart",u,Eo)}},[]);var m=e.removeScrollBar,w=e.inert;return f.createElement(f.Fragment,null,w?f.createElement(a,{styles:SP(o)}):null,m?f.createElement(vP,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function IP(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const AP=oP(O1,TP);var Ef=f.forwardRef(function(e,t){return f.createElement(Au,tn({},e,{ref:t,sideCar:AP}))});Ef.classNames=Au.classNames;var PP=function(e){if(typeof document>"u")return null;var t=Array.isArray(e)?e[0]:e;return t.ownerDocument.body},Io=new WeakMap,cc=new WeakMap,dc={},Fh=0,L1=function(e){return e&&(e.host||L1(e.parentNode))},OP=function(e,t){return t.map(function(n){if(e.contains(n))return n;var r=L1(n);return r&&e.contains(r)?r:(console.error("aria-hidden",n,"in not contained inside",e,". Doing nothing"),null)}).filter(function(n){return!!n})},DP=function(e,t,n,r){var o=OP(t,Array.isArray(e)?e:[e]);dc[n]||(dc[n]=new WeakMap);var a=dc[n],i=[],l=new Set,c=new Set(o),d=function(h){!h||l.has(h)||(l.add(h),d(h.parentNode))};o.forEach(d);var u=function(h){!h||c.has(h)||Array.prototype.forEach.call(h.children,function(p){if(l.has(p))u(p);else try{var m=p.getAttribute(r),w=m!==null&&m!=="false",x=(Io.get(p)||0)+1,b=(a.get(p)||0)+1;Io.set(p,x),a.set(p,b),i.push(p),x===1&&w&&cc.set(p,!0),b===1&&p.setAttribute(n,"true"),w||p.setAttribute(r,"true")}catch(v){console.error("aria-hidden: cannot operate on ",p,v)}})};return u(t),l.clear(),Fh++,function(){i.forEach(function(h){var p=Io.get(h)-1,m=a.get(h)-1;Io.set(h,p),a.set(h,m),p||(cc.has(h)||h.removeAttribute(r),cc.delete(h)),m||h.removeAttribute(n)}),Fh--,Fh||(Io=new WeakMap,Io=new WeakMap,cc=new WeakMap,dc={})}},F1=function(e,t,n){n===void 0&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=PP(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),DP(r,o,n,"aria-hidden")):function(){return null}},Pu="Dialog",[z1,l$]=Ra(Pu),[RP,Hs]=z1(Pu),B1=e=>{const{__scopeDialog:t,children:n,open:r,defaultOpen:o,onOpenChange:a,modal:i=!0}=e,l=f.useRef(null),c=f.useRef(null),[d,u]=kd({prop:r,defaultProp:o??!1,onChange:a,caller:Pu});return s.jsx(RP,{scope:t,triggerRef:l,contentRef:c,contentId:na(),titleId:na(),descriptionId:na(),open:d,onOpenChange:u,onOpenToggle:f.useCallback(()=>u(h=>!h),[u]),modal:i,children:n})};B1.displayName=Pu;var W1="DialogTrigger",U1=f.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=Hs(W1,n),a=We(t,o.triggerRef);return s.jsx(be.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.contentId,"data-state":Af(o.open),...r,ref:a,onClick:le(e.onClick,o.onOpenToggle)})});U1.displayName=W1;var Tf="DialogPortal",[MP,H1]=z1(Tf,{forceMount:void 0}),q1=e=>{const{__scopeDialog:t,forceMount:n,children:r,container:o}=e,a=Hs(Tf,t);return s.jsx(MP,{scope:t,forceMount:n,children:f.Children.map(r,i=>s.jsx(Ma,{present:n||a.open,children:s.jsx(bu,{asChild:!0,container:o,children:i})}))})};q1.displayName=Tf;var Id="DialogOverlay",V1=f.forwardRef((e,t)=>{const n=H1(Id,e.__scopeDialog),{forceMount:r=n.forceMount,...o}=e,a=Hs(Id,e.__scopeDialog);return a.modal?s.jsx(Ma,{present:r||a.open,children:s.jsx(LP,{...o,ref:t})}):null});V1.displayName=Id;var $P=ka("DialogOverlay.RemoveScroll"),LP=f.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=Hs(Id,n);return s.jsx(Ef,{as:$P,allowPinchZoom:!0,shards:[o.contentRef],children:s.jsx(be.div,{"data-state":Af(o.open),...r,ref:t,style:{pointerEvents:"auto",...r.style}})})}),po="DialogContent",G1=f.forwardRef((e,t)=>{const n=H1(po,e.__scopeDialog),{forceMount:r=n.forceMount,...o}=e,a=Hs(po,e.__scopeDialog);return s.jsx(Ma,{present:r||a.open,children:a.modal?s.jsx(FP,{...o,ref:t}):s.jsx(zP,{...o,ref:t})})});G1.displayName=po;var FP=f.forwardRef((e,t)=>{const n=Hs(po,e.__scopeDialog),r=f.useRef(null),o=We(t,n.contentRef,r);return f.useEffect(()=>{const a=r.current;if(a)return F1(a)},[]),s.jsx(Y1,{...e,ref:o,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:le(e.onCloseAutoFocus,a=>{var i;a.preventDefault(),(i=n.triggerRef.current)==null||i.focus()}),onPointerDownOutside:le(e.onPointerDownOutside,a=>{const i=a.detail.originalEvent,l=i.button===0&&i.ctrlKey===!0;(i.button===2||l)&&a.preventDefault()}),onFocusOutside:le(e.onFocusOutside,a=>a.preventDefault())})}),zP=f.forwardRef((e,t)=>{const n=Hs(po,e.__scopeDialog),r=f.useRef(!1),o=f.useRef(!1);return s.jsx(Y1,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:a=>{var i,l;(i=e.onCloseAutoFocus)==null||i.call(e,a),a.defaultPrevented||(r.current||(l=n.triggerRef.current)==null||l.focus(),a.preventDefault()),r.current=!1,o.current=!1},onInteractOutside:a=>{var c,d;(c=e.onInteractOutside)==null||c.call(e,a),a.defaultPrevented||(r.current=!0,a.detail.originalEvent.type==="pointerdown"&&(o.current=!0));const i=a.target;((d=n.triggerRef.current)==null?void 0:d.contains(i))&&a.preventDefault(),a.detail.originalEvent.type==="focusin"&&o.current&&a.preventDefault()}})}),Y1=f.forwardRef((e,t)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:o,onCloseAutoFocus:a,...i}=e,l=Hs(po,n),c=f.useRef(null),d=We(t,c);return I1(),s.jsxs(s.Fragment,{children:[s.jsx(Sf,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:o,onUnmountAutoFocus:a,children:s.jsx(Tl,{role:"dialog",id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":Af(l.open),...i,ref:d,onDismiss:()=>l.onOpenChange(!1)})}),s.jsxs(s.Fragment,{children:[s.jsx(BP,{titleId:l.titleId}),s.jsx(UP,{contentRef:c,descriptionId:l.descriptionId})]})]})}),If="DialogTitle",Q1=f.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=Hs(If,n);return s.jsx(be.h2,{id:o.titleId,...r,ref:t})});Q1.displayName=If;var K1="DialogDescription",X1=f.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=Hs(K1,n);return s.jsx(be.p,{id:o.descriptionId,...r,ref:t})});X1.displayName=K1;var J1="DialogClose",Z1=f.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=Hs(J1,n);return s.jsx(be.button,{type:"button",...r,ref:t,onClick:le(e.onClick,()=>o.onOpenChange(!1))})});Z1.displayName=J1;function Af(e){return e?"open":"closed"}var ek="DialogTitleWarning",[c$,tk]=X3(ek,{contentName:po,titleName:If,docsSlug:"dialog"}),BP=({titleId:e})=>{const t=tk(ek),n=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users. If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component. For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return f.useEffect(()=>{e&&(document.getElementById(e)||console.error(n))},[n,e]),null},WP="DialogDescriptionWarning",UP=({contentRef:e,descriptionId:t})=>{const r=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${tk(WP).contentName}}.`;return f.useEffect(()=>{var a;const o=(a=e.current)==null?void 0:a.getAttribute("aria-describedby");t&&o&&(document.getElementById(t)||console.warn(r))},[r,e,t]),null},sk=B1,HP=U1,nk=q1,Ou=V1,Du=G1,Ru=Q1,Mu=X1,rk=Z1;const qP=sk,VP=HP,GP=nk,ok=f.forwardRef(({className:e,...t},n)=>s.jsx(Ou,{className:Me("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",e),...t,ref:n}));ok.displayName=Ou.displayName;const YP=mf("fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",{variants:{side:{top:"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",bottom:"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",left:"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",right:"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm"}},defaultVariants:{side:"right"}}),ak=f.forwardRef(({side:e="right",className:t,children:n,...r},o)=>s.jsxs(GP,{children:[s.jsx(ok,{}),s.jsxs(Du,{ref:o,className:Me(YP({side:e}),t),...r,children:[n,s.jsxs(rk,{className:"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity data-[state=open]:bg-secondary hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none",children:[s.jsx($a,{className:"h-4 w-4"}),s.jsx("span",{className:"sr-only",children:"Close"})]})]})]}));ak.displayName=Du.displayName;const QP=f.forwardRef(({className:e,...t},n)=>s.jsx(Ru,{ref:n,className:Me("text-lg font-semibold text-foreground",e),...t}));QP.displayName=Ru.displayName;const KP=f.forwardRef(({className:e,...t},n)=>s.jsx(Mu,{ref:n,className:Me("text-sm text-muted-foreground",e),...t}));KP.displayName=Mu.displayName;const XP="/assets/nav-logo-small-CcthYWg5.png";function ik({className:e=""}){return s.jsx("img",{src:XP,alt:"Octomind",className:`h-8 ${e}`})}const JP="https://octoclaw.ai",_y="ph-banner-dismissed",lk=f.createContext(!1),ZP=()=>f.useContext(lk);function e8(){const[e,t]=f.useState(()=>typeof window<"u"&&localStorage.getItem(_y)!=="1");return{visible:e,dismiss:()=>{localStorage.setItem(_y,"1"),t(!1)},BannerContext:lk}}function t8(e){return s.jsxs("div",{className:"fixed top-0 left-0 right-0 z-[110] flex items-center justify-center px-4 py-2",style:{background:"linear-gradient(135deg, #ff6154 0%, #ff9543 100%)"},children:[s.jsx("a",{href:JP,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-white font-medium text-xs md:text-sm hover:opacity-90 transition-opacity",children:s.jsxs("span",{children:["🚀 ",s.jsx("strong",{children:"Launch Bonus:"})," Get OctoClaw for just $9.99/mo with code"," ",s.jsx("strong",{className:"bg-white/20 rounded px-1.5 py-0.5 mx-1 font-bold tracking-wide",children:"OCTOLOVE"})]})}),s.jsx("button",{type:"button",onClick:e.onDismiss,className:"absolute right-3 p-1 text-white/80 hover:text-white transition-colors","aria-label":"Dismiss banner",children:s.jsx($a,{className:"h-4 w-4"})})]})}function te({scrolled:e=!1}){const t=ZP();Fn();const[n,r]=f.useState(!1);f.useEffect(()=>{r(!0)},[]);const o=[{to:"/product/dev-mode/",label:"DEV mode"},{to:"/product/octomind-vs-coding-agents/",label:"Beyond Code Generation"},{to:"/product/playwright-self-healing/",label:"Self-Healing"},{to:"/product/mcp/",label:"MCP Server"}],a=[{to:"/case-studies/",label:"CASE-STUDIES"},{to:"/blog/",label:"BLOG"},{to:"/about/",label:"ABOUT"}];return s.jsx(s.Fragment,{children:s.jsx("nav",{className:`fixed left-0 right-0 z-50 transition-all duration-300 ${t?"top-10":"top-0"} ${e?"py-3 backdrop-blur-xl bg-octo-dark/90 border-b border-white/10":"backdrop-blur-xl bg-octo-dark/90 border-b border-white/10"}`,children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6 py-4 flex justify-between items-center",children:[s.jsx(Fs,{to:"/",children:s.jsx(ik,{className:"h-8"})}),s.jsxs("div",{className:"hidden md:flex items-center gap-8",children:[s.jsxs("div",{className:"relative group",children:[s.jsxs("button",{className:"flex items-center gap-1 text-xs font-normal text-octo-text hover:text-white transition-colors outline-none bg-transparent border-none cursor-pointer",children:["PRODUCT",s.jsx(Al,{className:"h-3 w-3"})]}),s.jsx("div",{className:"absolute left-0 top-full pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible group-focus-within:opacity-100 group-focus-within:visible transition-all duration-200 z-50",children:s.jsx("div",{className:"bg-octo-dark border border-white/10 rounded-md py-1 min-w-[180px] shadow-lg",children:o.map(i=>s.jsx("a",{href:i.to,className:"block px-4 py-2 text-xs text-octo-text hover:text-white hover:bg-white/10 transition-colors",children:i.label},i.to))})})]}),a.map(i=>s.jsx(Fs,{to:i.to,className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:i.label},i.to)),s.jsx("a",{href:"https://octomind.dev/docs/get-started/create-first-tests",target:"_blank",rel:"noopener noreferrer",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"DOCS"}),s.jsx("div",{className:"flex items-center gap-3",children:s.jsx(ut,{variant:"secondary",className:"px-6 py-2 rounded-xl text-xs font-normal",onClick:()=>window.open("https://auth.app.octomind.dev/","_blank"),children:"LOGIN"})})]}),s.jsxs(qP,{children:[s.jsx(VP,{asChild:!0,className:"md:hidden",children:s.jsx(ut,{variant:"ghost",size:"icon",className:"text-white hover:bg-octo-purple hover:text-white",children:s.jsx(t6,{className:"h-6 w-6"})})}),n?s.jsx(ak,{side:"right",className:"bg-octo-dark border-white/10 w-[280px]",children:s.jsxs("div",{className:"flex flex-col gap-6 mt-8",children:[s.jsxs("div",{className:"flex flex-col gap-2",children:[s.jsx("span",{className:"text-sm font-normal text-white",children:"PRODUCT"}),o.map(i=>s.jsx("a",{href:i.to,className:"text-sm font-normal text-octo-text hover:text-white transition-colors pl-4",children:i.label},i.to))]}),a.map(i=>s.jsx(Fs,{to:i.to,className:"text-sm font-normal text-octo-text hover:text-white transition-colors",children:i.label},i.to)),s.jsx("a",{href:"https://octomind.dev/docs/get-started/create-first-tests",target:"_blank",rel:"noopener noreferrer",className:"text-sm font-normal text-octo-text hover:text-white transition-colors",children:"DOCS"}),s.jsx(ut,{variant:"secondary",className:"px-6 py-2 rounded-xl text-sm font-normal w-full mt-4",onClick:()=>window.open("https://auth.app.octomind.dev/","_blank"),children:"LOGIN"})]})}):null]})]})})})}function se(){return s.jsx("footer",{className:"bg-octo-dark border-t border-white/10 py-12 relative z-10",children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6",children:[s.jsxs("div",{className:"grid md:grid-cols-3 gap-8",children:[s.jsxs("div",{children:[s.jsx(ik,{className:"h-8 mb-4"}),s.jsx("p",{className:"text-xs font-normal text-octo-text",children:"Automated testing for ambitious teams."})]}),s.jsxs("div",{children:[s.jsx("h4",{className:"text-sm font-normal text-white mb-4",children:"HELP"}),s.jsxs("ul",{className:"space-y-2",children:[s.jsx("li",{children:s.jsx("a",{href:"https://octomind.dev/docs/get-started/create-first-tests",target:"_blank",rel:"noopener noreferrer",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"Docs"})}),s.jsx("li",{children:s.jsx("a",{href:"mailto:contact@octomind.dev",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"Contact"})})]})]}),s.jsxs("div",{children:[s.jsx("h4",{className:"text-sm font-normal text-white mb-4",children:"LEGAL"}),s.jsxs("ul",{className:"space-y-2",children:[s.jsx("li",{children:s.jsx(Fs,{to:"/privacy",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"Privacy Policy"})}),s.jsx("li",{children:s.jsx(Fs,{to:"/legal",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"Legal / Impressum"})}),s.jsx("li",{children:s.jsx("a",{href:"https://trust.octomind.dev/",target:"_blank",rel:"noopener noreferrer",className:"text-xs font-normal text-octo-text hover:text-white transition-colors",children:"Trust Center"})})]})]})]}),s.jsx("div",{className:"mt-12 pt-8 border-t border-white/10",children:s.jsxs("div",{className:"flex items-center justify-center md:justify-between",children:[s.jsxs("p",{className:"text-xs font-normal text-octo-text",children:["© ",2026," Octomind. All rights reserved."]}),s.jsxs("div",{className:"hidden md:flex gap-6",children:[s.jsx("a",{href:"https://github.com/OctoMind-dev",target:"_blank",rel:"noopener noreferrer",className:"opacity-70 hover:opacity-100 transition-opacity",children:s.jsx("img",{src:"/assets/icons/github.svg",alt:"GitHub",className:"h-5 w-5"})}),s.jsx("a",{href:"https://x.com/octominddotdev",target:"_blank",rel:"noopener noreferrer",className:"opacity-70 hover:opacity-100 transition-opacity",children:s.jsx("img",{src:"/assets/icons/twitter.svg",alt:"X (Twitter)",className:"h-5 w-5"})}),s.jsx("a",{href:"https://www.linkedin.com/company/octomind-dev/",target:"_blank",rel:"noopener noreferrer",className:"opacity-70 hover:opacity-100 transition-opacity",children:s.jsx("img",{src:"/assets/icons/linkedin.svg",alt:"LinkedIn",className:"h-5 w-5"})}),s.jsx("a",{href:"https://www.youtube.com/@octomind-dev",target:"_blank",rel:"noopener noreferrer",className:"opacity-70 hover:opacity-100 transition-opacity",children:s.jsx("img",{src:"/assets/icons/youtube.svg",alt:"YouTube",className:"h-5 w-5"})})]}),s.jsx("div",{className:"hidden md:block",children:s.jsx("iframe",{src:"https://status.octomind.dev/badge?theme=dark",width:"250",height:"30",frameBorder:"0",scrolling:"no",style:{colorScheme:"normal"},title:"Service status"})})]})})]})})}const s8=[{src:"/assets/logos/brm.png",alt:"BRM"},{src:"/assets/logos/deriv.png",alt:"Deriv"},{src:"/assets/logos/fixify.png",alt:"Fixify"},{src:"/assets/logos/wingrep.png",alt:"Wingrep"},{src:"/assets/logos/hadrian.png",alt:"Hadrian"},{src:"/assets/logos/lennar.png",alt:"Lennar"}];function Ol(){return s.jsx("section",{className:"py-16 md:py-20 px-4 md:px-6 relative border-y border-white/5 bg-gradient-to-b from-[#19233B]/40 to-transparent",children:s.jsxs("div",{className:"max-w-7xl mx-auto",children:[s.jsx("div",{className:"text-center mb-12",children:s.jsx("p",{className:"text-xs font-normal text-octo-text uppercase tracking-widest",children:"Trusted by hundreds of teams around the world"})}),s.jsx("div",{className:"grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-12 md:gap-16 items-center justify-items-center",children:s8.map((e,t)=>s.jsx("div",{className:"hover:opacity-100 transition-opacity duration-300",children:s.jsx("img",{src:e.src,alt:e.alt,className:"h-8 md:h-10 w-auto object-contain grayscale hover:grayscale-0 transition-all duration-300"})},t))})]})})}const n8=[{icon:r6,title:"Creation",subtitle:"Instant test code from simple actions.",description:"Product, QA, and engineering can all create test cases visually, while Octomind handles the code, structure, and best practices.",image:"/assets/pillars/creation.png"},{icon:s6,title:"Execution",subtitle:"Local or cloud — your choice.",description:"Run tests seamlessly across your preferred environment while Octomind ensures stable, reproducible results.",image:"/assets/pillars/execution.png"},{icon:n6,title:"Debugging",subtitle:"See exactly what went wrong.",description:"Octomind highlights failures with screenshots, logs, and visual diffs so you can pinpoint issues instantly.",image:"/assets/pillars/debugging.png"},{icon:l6,title:"Maintenance",subtitle:"Your tests fix themselves.",description:"Octomind automatically updates selectors and flows, keeping your test suite stable without manual effort.",image:"/assets/pillars/maintenance.png"}];function r8(){const[e,t]=f.useState(null),[n,r]=f.useState(!1),o=()=>{r(!0),setTimeout(()=>{t(null),r(!1)},200)};return s.jsxs("section",{className:"py-16 md:py-24 px-4 md:px-6 relative",children:[s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-black text-white mb-2",children:"all you need for stable, scalable e2e tests"}),s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-octo-teal to-octo-purple mb-6",children:"in your workflow"}),s.jsxs("p",{className:"text-lg md:text-xl font-normal text-octo-text",children:["You get fast creation, stable execution, instant debugging, and self-healing maintenance",s.jsx("br",{}),"so your tests stay reliable and your pipeline stays green."]})]}),s.jsx("div",{className:"grid md:grid-cols-2 gap-6",children:n8.map(a=>s.jsxs("div",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-6 md:p-8 hover:border-white/20 transition-all cursor-pointer",onClick:()=>t(a.image),children:[s.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[s.jsx(a.icon,{className:"w-5 h-5 text-octo-teal"}),s.jsx("h3",{className:"text-xl font-bold text-white",children:a.title})]}),s.jsx("p",{className:"text-octo-teal font-medium mb-2",children:a.subtitle}),s.jsx("p",{className:"text-sm text-octo-text mb-6 leading-relaxed",children:a.description}),s.jsxs("div",{className:"relative h-40 overflow-hidden rounded-lg",children:[s.jsx("img",{src:a.image,alt:`${a.title} screenshot`,className:"w-full h-auto object-cover object-top transition-transform duration-300 group-hover:scale-105"}),s.jsx("div",{className:"absolute inset-0 bg-gradient-to-t from-octo-card via-transparent to-transparent pointer-events-none"}),s.jsx("div",{className:"absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200",children:s.jsx("span",{className:"bg-white/10 backdrop-blur-sm px-4 py-2 rounded-lg text-sm text-white",children:"Click to view full image"})})]})]},a.title))})]}),e&&s.jsx("div",{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/90 backdrop-blur-sm p-4 ${n?"animate-fade-out":"animate-fade-in"}`,onClick:o,children:s.jsxs("div",{className:`relative max-w-7xl max-h-[90vh] w-full ${n?"animate-scale-out":"animate-scale-in"}`,onClick:a=>a.stopPropagation(),children:[s.jsx("button",{onClick:o,className:"absolute -top-12 right-0 p-2 text-white/70 hover:text-white transition-colors",children:s.jsx($a,{className:"w-8 h-8"})}),s.jsx("img",{src:e,alt:"Full size screenshot",className:"w-full h-auto max-h-[90vh] object-contain rounded-lg"})]})})]})}const ck="/assets/mark-odonnell-D9VKBW8R.png",o8="/assets/anom-chavez-DqnRTYL-.jpeg",dk="/assets/fabian-frank-fC25MxDL.jpeg",a8="/assets/klaus-krogmann-CRhXTiZp.png",i8="/assets/georgi-urumov-BTQo4S5M.jpeg",uc=[{quote:"It is what sets Octomind apart from other LLM-based tools. It's repeatable and reliable.",author:"Mark O'Donnell",role:"Principal Engineer",company:"Deriv",image:ck,linkedin:"https://linkedin.com/in/mark-o-donnell-a1716a95/"},{quote:"The visual test history is incredibly helpful, I can quickly confirm test health and debug failures fast.",author:"Anom Chavez",role:"QA Engineer",company:"WingRep",image:o8,linkedin:"https://linkedin.com/in/anomchavez/"},{quote:"Auto-fix and maintenance is why I buy Octomind.",author:"Fabian Frank",role:"CTO & co-founder",company:"BRM",image:dk,linkedin:"https://linkedin.com/in/fabianfrank87/"},{quote:"I love how quickly Octomind evolves and improves. It saves QA time and directs us right to the bugs with AI-generated executable test cases.",author:"Klaus Krogmann",role:"Director AI & Software Engineering",company:"GoTo",image:a8,linkedin:"https://linkedin.com/in/klauskrogmann/"},{quote:"Awesome product! Even handles obscure OTP code libraries! Now I hate tests a bit less :)",author:"Georgi Urumov",role:"CTO",company:"Mentalyc",image:i8,linkedin:"https://linkedin.com/in/georgi-urumov-95545b4a/"}];function l8(){const e=r=>{const o=[-6,-3,0,3,6];return o[r%o.length]},n=25/uc.length;return s.jsxs("section",{className:"py-16 md:py-24 px-4 md:px-6 relative",children:[s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsx("div",{className:"text-center mb-12",children:s.jsx("h2",{className:"text-4xl md:text-5xl font-black text-white mb-6",children:"loved by engineering teams"})}),s.jsxs("div",{className:"testimonials-carousel",children:[uc.map((r,o)=>s.jsx("input",{type:"radio",name:"testimonial",id:`testimonial-${o}`,className:"sr-only testimonial-radio",defaultChecked:o===0},`radio-${o}`)),s.jsxs("div",{className:"testimonials-stack relative h-[320px] md:h-[280px] flex items-center justify-center",children:[[...Array(3)].map((r,o)=>s.jsx("div",{className:"absolute w-full max-w-lg bg-octo-card/30 border border-white/5 rounded-2xl h-[260px] md:h-[220px]",style:{transform:`rotate(${(o-1)*4}deg) translateY(${o*4}px)`,zIndex:o}},`bg-${o}`)),uc.map((r,o)=>s.jsx("div",{className:"testimonial-card absolute w-full max-w-lg",style:{zIndex:10,"--rotation":`${e(o)}deg`,"--delay":`${o*n}s`},"data-index":o,children:s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/80 border border-white/10 rounded-2xl p-6 md:p-8 shadow-2xl",style:{boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.05)"},children:[s.jsxs("p",{className:"text-lg md:text-xl font-normal text-white mb-6 leading-relaxed italic",children:['"',r.quote,'"']}),s.jsxs("a",{href:r.linkedin,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-4 group",children:[s.jsx("img",{src:r.image,alt:r.author,className:"w-14 h-14 rounded-full object-cover border-2 border-white/20 group-hover:border-octo-teal/50 transition-colors"}),s.jsxs("div",{children:[s.jsx("p",{className:"text-base font-bold text-white group-hover:text-octo-teal transition-colors",children:r.author}),s.jsxs("p",{className:"text-sm font-normal text-octo-text",children:[r.role,", ",r.company]})]})]})]})},o))]}),s.jsx("div",{className:"flex justify-center gap-2 mt-8",children:uc.map((r,o)=>s.jsx("label",{htmlFor:`testimonial-${o}`,className:"testimonial-dot w-2 h-2 rounded-full bg-white/30 hover:bg-white/50 cursor-pointer transition-all duration-300",style:{"--delay":`${o*n}s`},"data-index":o,"aria-label":`Go to testimonial ${o+1}`},o))})]})]}),s.jsx("style",{children:` /* Smooth crossfade animation - matches JS version timing */ @keyframes testimonial-crossfade { 0% { opacity: 0; transform: rotate(var(--rotation)) scale(0.95); } 2% { opacity: 1; transform: rotate(var(--rotation)) scale(1); } 18% { opacity: 1; transform: rotate(var(--rotation)) scale(1); } 20% { opacity: 0; transform: rotate(var(--rotation)) scale(0.95); } 100% { opacity: 0; transform: rotate(var(--rotation)) scale(0.95); } } @keyframes dot-pulse { 0% { background-color: rgba(255, 255, 255, 0.3); width: 0.5rem; } 2% { background-color: #00AF93; width: 1.5rem; } 18% { background-color: #00AF93; width: 1.5rem; } 20% { background-color: rgba(255, 255, 255, 0.3); width: 0.5rem; } 100% { background-color: rgba(255, 255, 255, 0.3); width: 0.5rem; } } /* Default state: auto-rotation active */ .testimonial-card { opacity: 0; transform: rotate(var(--rotation)) scale(0.95); animation: testimonial-crossfade 25s infinite; animation-delay: var(--delay); pointer-events: none; transition: opacity 0.5s ease, transform 0.5s ease; } .testimonial-dot { animation: dot-pulse 25s infinite; animation-delay: var(--delay); } /* When visible, enable pointer events */ .testimonial-card[style*="opacity: 1"], .testimonials-carousel:not(:has(.testimonial-radio:checked)) .testimonial-card { pointer-events: auto; } /* Manual control: when any radio is actively clicked, stop animations */ .testimonials-carousel:has(.testimonial-radio:focus) .testimonial-card, .testimonials-carousel:has(.testimonial-radio:focus) .testimonial-dot { animation: none; } /* Manual selection overrides */ .testimonials-carousel:has(#testimonial-0:checked:not(:focus)) .testimonial-card, .testimonials-carousel:has(#testimonial-1:checked:not(:focus)) .testimonial-card, .testimonials-carousel:has(#testimonial-2:checked:not(:focus)) .testimonial-card, .testimonials-carousel:has(#testimonial-3:checked:not(:focus)) .testimonial-card, .testimonials-carousel:has(#testimonial-4:checked:not(:focus)) .testimonial-card { /* Keep animation running by default */ } /* When user clicks a dot, show that specific testimonial */ .testimonials-carousel:has(.testimonial-radio:active) .testimonial-card, .testimonials-carousel:has(.testimonial-radio:active) .testimonial-dot { animation: none !important; } .testimonials-carousel:has(#testimonial-0:active) .testimonial-card[data-index="0"], .testimonials-carousel:has(#testimonial-1:active) .testimonial-card[data-index="1"], .testimonials-carousel:has(#testimonial-2:active) .testimonial-card[data-index="2"], .testimonials-carousel:has(#testimonial-3:active) .testimonial-card[data-index="3"], .testimonials-carousel:has(#testimonial-4:active) .testimonial-card[data-index="4"] { opacity: 1 !important; transform: rotate(var(--rotation)) scale(1) !important; pointer-events: auto !important; } .testimonials-carousel:has(#testimonial-0:active) .testimonial-dot[data-index="0"], .testimonials-carousel:has(#testimonial-1:active) .testimonial-dot[data-index="1"], .testimonials-carousel:has(#testimonial-2:active) .testimonial-dot[data-index="2"], .testimonials-carousel:has(#testimonial-3:active) .testimonial-dot[data-index="3"], .testimonials-carousel:has(#testimonial-4:active) .testimonial-dot[data-index="4"] { background-color: #00AF93 !important; width: 1.5rem !important; } /* Pause on hover for readability */ .testimonials-carousel:hover .testimonial-card, .testimonials-carousel:hover .testimonial-dot { animation-play-state: paused; } /* Reduce motion for accessibility */ @media (prefers-reduced-motion: reduce) { .testimonial-card, .testimonial-dot { animation: none !important; } .testimonial-card[data-index="0"] { opacity: 1; transform: rotate(var(--rotation)) scale(1); pointer-events: auto; } .testimonial-dot[data-index="0"] { background-color: #00AF93; width: 1.5rem; } } `})]})}const Cy=e=>{const t=String(e.getMonth()+1).padStart(2,"0"),n=String(e.getDate()).padStart(2,"0");return`${t}/${n}`},c8=e=>e.toString().replace(/\B(?=(\d{3})+(?!\d))/g,","),Ft={background:"#13142B",textMain:"#FFFFFF",textSub:"#6D7D97",passed:"#00AF93",failed:"#BC344B"},Sy=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,d8=e=>{const t=[],n=new Date;n.setHours(0,0,0,0);for(let r=e-1;r>=0;r--){const o=new Date(n);o.setDate(n.getDate()-r);const a=o.getDay(),i=a===0||a===6;let l=Sy(3500,4e3);i&&(l=Math.floor(l*.2));const c=Sy(10,20)/1e3,d=Math.ceil(l*c),u=l-d;t.push({date:o,passed:u,failed:d,total:l})}return t},u8=e=>Array.from({length:e},(o,a)=>({date:new Date(2e3,0,a+1),passed:3745,failed:55,total:3800})),h8=()=>{const[e,t]=f.useState(!1),[n,r]=f.useState(()=>u8(30));f.useEffect(()=>{t(!0),r(d8(30))},[]);const o=f.useMemo(()=>{const l=n.reduce((d,u)=>d+u.total,0),c=n.reduce((d,u)=>d+u.passed,0);return Math.round(c/l*100)},[n]),a=4e3,i=200;return s.jsxs("div",{className:"w-full max-w-5xl h-full rounded-[26px] p-8 font-sans flex flex-col",style:{backgroundColor:Ft.background},children:[s.jsxs("div",{className:"flex justify-between items-start mb-6",children:[s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[s.jsx("h2",{className:"text-2xl font-normal tracking-wide lowercase",style:{color:Ft.textMain},children:"project health"}),s.jsxs("div",{className:"relative group/info",children:[s.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:Ft.textMain,strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"opacity-70 cursor-help",children:[s.jsx("circle",{cx:"12",cy:"12",r:"10"}),s.jsx("line",{x1:"12",y1:"16",x2:"12",y2:"12"}),s.jsx("line",{x1:"12",y1:"8",x2:"12.01",y2:"8"})]}),s.jsxs("div",{className:"absolute left-1/2 -translate-x-1/2 top-full mt-2 hidden group-hover/info:block bg-[#1a1b2e] text-white text-sm p-3 rounded-lg whitespace-nowrap z-10 shadow-lg border border-white/10 max-w-[280px]",children:[s.jsx("div",{className:"absolute -top-2 left-1/2 -translate-x-1/2 w-0 h-0 border-l-[8px] border-l-transparent border-r-[8px] border-r-transparent border-b-[8px] border-b-[#1a1b2e]"}),s.jsx("p",{className:"whitespace-normal text-white/90",children:"project health represents the number of successful and failed test report executions over time."})]})]})]}),s.jsx("p",{className:"text-sm font-normal",style:{color:Ft.textSub},children:"failed / passed test cases over time"})]}),s.jsxs("div",{className:"relative group/percent",children:[s.jsxs("div",{className:"text-6xl font-black tracking-tight cursor-default",style:{color:Ft.passed},children:[o,"%"]}),s.jsxs("div",{className:"absolute left-1/2 -translate-x-1/2 top-full mt-2 hidden group-hover/percent:block bg-[#1a1b2e] text-white text-sm p-3 rounded-lg whitespace-nowrap z-10 shadow-lg border border-white/10",children:[s.jsx("div",{className:"absolute -top-2 left-1/2 -translate-x-1/2 w-0 h-0 border-l-[8px] border-l-transparent border-r-[8px] border-r-transparent border-b-[8px] border-b-[#1a1b2e]"}),s.jsx("p",{className:"text-white/90",children:e?`test success rate on ${Cy(new Date)}`:"test success rate"})]})]})]}),s.jsxs("div",{className:"flex justify-center gap-6 mb-8 text-sm",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"w-3 h-3 rounded-[2px]",style:{backgroundColor:Ft.passed}}),s.jsx("span",{style:{color:Ft.textMain},children:"passed"})]}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"w-3 h-3 rounded-[2px]",style:{backgroundColor:Ft.failed}}),s.jsx("span",{style:{color:Ft.textMain},children:"failed"})]})]}),s.jsxs("div",{className:"relative w-full flex-1 flex pb-4",children:[s.jsxs("div",{className:"flex flex-col justify-between items-end pr-4 text-xs w-12 shrink-0",style:{color:Ft.textSub,height:`${i}px`},children:[s.jsx("span",{children:"4,000"}),s.jsx("span",{children:"3,000"}),s.jsx("span",{children:"2,000"}),s.jsx("span",{children:"1,000"}),s.jsx("span",{children:"0"})]}),s.jsx("div",{className:"flex items-end justify-between w-full gap-1",style:{height:`${i}px`},children:n.map((l,c)=>{const d=l.passed/a*i,u=l.failed/a*i;return s.jsxs("div",{className:"relative flex flex-col justify-end w-full group",style:{height:`${i}px`},children:[s.jsxs("div",{className:"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 hidden group-hover:block bg-[#1a1b2e] text-white text-xs p-3 rounded-lg whitespace-nowrap z-10 shadow-lg border border-white/10",children:[s.jsx("div",{className:"text-sm font-medium mb-2",children:e?Cy(l.date):"--/--"}),s.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[s.jsx("span",{className:"w-3 h-3 rounded-[2px]",style:{backgroundColor:Ft.passed}}),s.jsx("span",{className:"text-white/80",children:"passed"}),s.jsx("span",{className:"ml-auto font-medium",children:c8(l.passed)})]}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"w-3 h-3 rounded-[2px]",style:{backgroundColor:Ft.failed}}),s.jsx("span",{className:"text-white/80",children:"failed"}),s.jsx("span",{className:"ml-auto font-medium",children:l.failed})]})]}),s.jsxs("div",{className:"w-full flex flex-col-reverse",children:[l.passed>0&&s.jsx("div",{className:"w-full rounded-b-[2px] origin-bottom",style:{height:`${d}px`,backgroundColor:Ft.passed,borderTopLeftRadius:l.failed===0?"2px":"0",borderTopRightRadius:l.failed===0?"2px":"0",animation:`growBar 0.5s ease-out ${c*.03}s both`}}),l.failed>0&&s.jsx("div",{className:"w-full rounded-t-[2px] origin-bottom",style:{height:`${u}px`,backgroundColor:Ft.failed,borderBottomLeftRadius:l.passed===0?"2px":"0",borderBottomRightRadius:l.passed===0?"2px":"0",animation:`growBar 0.5s ease-out ${c*.03}s both`}})]})]},c)})})]})]})},m8="/assets/octoclaw-logo-Du02fGLY.png",Ey="octoclaw-popup-dismissed";function p8(){const[e,t]=f.useState(!1);f.useEffect(()=>{if(localStorage.getItem(Ey))return;const r=setTimeout(()=>t(!0),1500);return()=>clearTimeout(r)},[]);const n=()=>{t(!1),localStorage.setItem(Ey,"1")};return e?s.jsx("div",{className:"fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-fade-in",children:s.jsxs("div",{className:"relative w-full max-w-[500px] rounded-2xl border border-octo-purple/30 bg-[#1a1d27] px-8 pb-8 pt-10 shadow-[0_24px_64px_rgba(0,0,0,0.5),0_0_0_1px_rgba(151,103,228,0.1)] animate-scale-in",onClick:r=>r.stopPropagation(),children:[s.jsx("button",{onClick:n,className:"absolute top-4 right-4 text-white/30 hover:text-white/60 transition-colors",children:s.jsx($a,{className:"h-5 w-5"})}),s.jsx("span",{className:"inline-block mb-3 rounded-full bg-octo-purple/15 px-3 py-1 text-[0.72rem] font-semibold uppercase tracking-wider text-octo-purple",children:"From the Octomind team"}),s.jsx("h2",{className:"mb-3 text-[1.4rem] font-bold leading-tight text-white",children:"We built something new."}),s.jsxs("div",{className:"mb-6 flex items-start gap-4",children:[s.jsx("img",{src:m8,alt:"OctoClaw",className:"h-14 w-14 flex-shrink-0 object-contain mt-0.5"}),s.jsxs("p",{className:"text-[0.95rem] leading-relaxed text-octo-text",children:["Meet ",s.jsx("strong",{className:"text-white/90",children:"OctoClaw"})," your personal AI agent that works 24/7 so you don't have to. No terminal. No config. Just results."]})]}),s.jsx("a",{href:"https://octoclaw.ai?utm_source=octomind-popup",target:"_blank",rel:"noopener noreferrer",className:"block w-full rounded-[10px] bg-octo-purple py-3.5 text-center text-[0.95rem] font-semibold text-white transition-all hover:bg-octo-purple-dark hover:-translate-y-px active:translate-y-0",children:"Check out OctoClaw →"}),s.jsx("button",{onClick:n,className:"mt-5 pb-3 block w-full text-center text-sm text-[#666] hover:text-[#888] transition-colors",children:"Maybe later"})]})}):null}const uk=sk,x8=nk,hk=f.forwardRef(({className:e,...t},n)=>s.jsx(Ou,{ref:n,className:Me("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",e),...t}));hk.displayName=Ou.displayName;const Pf=f.forwardRef(({className:e,children:t,variant:n="default",...r},o)=>s.jsxs(x8,{children:[s.jsx(hk,{}),s.jsxs(Du,{ref:o,className:Me("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",n==="white"?"bg-white text-gray-900 border-gray-200":"bg-background p-6",e),...r,children:[t,s.jsxs(rk,{className:Me("absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground",n==="white"&&"text-gray-500 hover:text-gray-900"),children:[s.jsx($a,{className:"h-4 w-4"}),s.jsx("span",{className:"sr-only",children:"Close"})]})]})]}));Pf.displayName=Du.displayName;const mk=({className:e,...t})=>s.jsx("div",{className:Me("flex flex-col space-y-1.5 text-center sm:text-left",e),...t});mk.displayName="DialogHeader";const pk=f.forwardRef(({className:e,...t},n)=>s.jsx(Ru,{ref:n,className:Me("text-lg font-semibold leading-none tracking-tight",e),...t}));pk.displayName=Ru.displayName;const f8=f.forwardRef(({className:e,...t},n)=>s.jsx(Mu,{ref:n,className:Me("text-sm text-muted-foreground",e),...t}));f8.displayName=Mu.displayName;function g8(){Fn();const[e,t]=f.useState(!1),[n,r]=f.useState(null),[o,a]=f.useState(!1),i=f.useRef(null),l=u=>{i.current&&(clearTimeout(i.current),i.current=null),r(u)},c=()=>{i.current=setTimeout(()=>{r(null)},150)},d=[{question:"What is Octomind?",answer:"Octomind is an AI-powered QA platform that creates, runs, and auto-fixes end-to-end tests for your web app — no coding or QA engineer required. It integrates directly into your CI/CD workflow and ensures every core user journey stays intact across releases."},{question:"Who is Octomind built for?",answer:"Octomind is designed for early-stage and fast-growing SaaS or AI startups with small engineering teams that ship multiple times a week. If you're moving fast, don't have dedicated QA, and are tired of firefighting production bugs, Octomind is for you."},{question:"How long does it take to set up and see results?",answer:"Setup takes under 5 minutes, and you'll start seeing automated test results immediately after your next pull request. No complex configuration or test scripting required."},{question:"How does Octomind improve product quality and speed?",answer:"Octomind automatically protects your core user flows with stable, self-healing tests that run on every deploy. As coverage increases, it catches regressions before they reach users — reducing bugs, support tickets, and sleepless nights while helping your team ship faster and more confidently."},{question:"Why should I trust Octomind?",answer:"Octomind is trusted by thousands of engineering teams worldwide, integrates seamlessly with Playwright, Cypress, and modern CI/CD pipelines, and is SOC-2 certified. There's no vendor lock-in — your code and tests remain fully yours."}];return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsx(p8,{}),s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("section",{className:"min-h-[85vh] flex items-center justify-center py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-7xl mx-auto relative z-10 w-full",children:[s.jsx("div",{className:"text-center mb-12 animate-fade-in-up",children:s.jsxs("h1",{className:"mb-6 leading-tight text-4xl md:text-5xl font-black text-white",children:["automated e2e-tests at scale",s.jsx("br",{}),s.jsx("span",{className:"text-transparent bg-clip-text bg-gradient-to-r from-octo-teal to-octo-purple",children:"catch bugs before your users do"})]})}),s.jsxs("div",{className:"grid md:grid-cols-2 gap-8 lg:gap-12 mt-12 items-start",children:[s.jsxs("div",{className:"flex flex-col animate-fade-in-up",children:[s.jsx("p",{className:"text-xl font-normal text-octo-text mb-6",children:"Everything your e2e setup needs - smarter runs, clearer insights, and dependable results starting on day one."}),s.jsx("div",{className:"flex flex-col gap-3",children:[{prefix:"Tired of stepping through broken logs?",highlight:"Persistent traces cut debugging time by 50%."},{prefix:"Sick of flaky tests?",highlight:"Self-healing reduces false positives by 78%."},{prefix:"Struggling with coverage?",highlight:"AI helps your whole team add and adapt tests"}].map((u,h)=>s.jsxs("div",{className:`flex items-center gap-3 p-2 -ml-2 rounded-lg cursor-pointer transition-colors duration-200 ${n===h?"bg-white/10":"hover:bg-white/5"}`,onMouseEnter:()=>l(h),onMouseLeave:c,children:[s.jsx(ON,{className:`w-5 h-5 flex-shrink-0 transition-colors duration-200 ${n===h?"text-white":"text-octo-teal"}`}),s.jsxs("span",{className:"text-sm text-white",children:[u.prefix," ",s.jsx("span",{className:"font-bold",children:u.highlight})]})]},h))}),s.jsxs("div",{className:"mt-10 relative",children:[s.jsxs("div",{className:`max-w-[65%] p-4 backdrop-blur-xl bg-octo-card/40 border border-white/10 rounded-xl rotate-[-6deg] transition-all duration-200 relative ${o?"z-20":"z-10"}`,onMouseEnter:()=>a(!0),onMouseLeave:()=>a(!1),children:[s.jsx("p",{className:"text-sm italic text-octo-text mb-3",children:`"It is what sets Octomind apart from other LLM-based tools. It's repeatable and reliable."`}),s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx("img",{src:ck,alt:"Mark O'Donnell",className:"w-8 h-8 rounded-full object-cover"}),s.jsxs("div",{children:[s.jsx("p",{className:"text-xs font-medium text-white",children:"Mark O'Donnell"}),s.jsx("p",{className:"text-xs text-octo-text",children:"Principal Engineer, Deriv"})]})]})]}),s.jsxs("div",{className:`max-w-[55%] p-4 backdrop-blur-xl bg-octo-card/40 border border-white/10 rounded-xl rotate-[4deg] absolute -bottom-12 right-0 ${o?"z-10":"z-20"}`,children:[s.jsx("p",{className:"text-sm italic text-octo-text mb-3",children:'"Auto-fix and maintenance is why I buy Octomind."'}),s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx("img",{src:dk,alt:"Fabian Frank",className:"w-8 h-8 rounded-full object-cover"}),s.jsxs("div",{children:[s.jsx("p",{className:"text-xs font-medium text-white",children:"Fabian Frank"}),s.jsx("p",{className:"text-xs text-octo-text",children:"CTO & co-founder, BRM"})]})]})]})]})]}),s.jsx("div",{className:"hidden md:flex h-[420px] animate-fade-in-up",children:n===null?s.jsx(h8,{}):n===0?s.jsxs("div",{className:"w-full h-full rounded-[26px] p-8 flex flex-col overflow-hidden animate-fade-in-up",style:{backgroundColor:"#13142B"},children:[s.jsx("h3",{className:"text-2xl font-normal tracking-wide lowercase text-white mb-3",children:"cut debugging time in half"}),s.jsx("p",{className:"text-sm text-octo-text mb-4",children:"Persistent run history and its traces let you compare failed and successful runs side-by-side, helping you debug faster and with far less pain."}),s.jsxs("div",{className:"relative flex-1 overflow-hidden rounded-lg",children:[s.jsx("img",{src:"/assets/pillars/persistent-traces.png",alt:"Persistent traces - run history comparison",className:"w-full h-full object-cover object-top"}),s.jsx("div",{className:"absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-[#13142B]"})]})]},"persistent-traces"):n===1?s.jsxs("div",{className:"w-full h-full rounded-[26px] p-8 flex flex-col overflow-hidden animate-fade-in-up",style:{backgroundColor:"#13142B"},children:[s.jsx("h3",{className:"text-2xl font-normal tracking-wide lowercase text-white mb-3",children:"cut false-positives by 78%"}),s.jsx("p",{className:"text-sm text-octo-text mb-4",children:"Your tests stay stable with intelligent waits, resilient locators, self-healing on failures, and a quarantine for flaky cases - so flakiness stops slowing you down."}),s.jsxs("div",{className:"relative flex-1 overflow-hidden rounded-lg",children:[s.jsx("img",{src:"/assets/pillars/flakiness-prevention.png",alt:"Flakiness prevention - auto-fix approval",className:"w-full h-full object-cover object-top"}),s.jsx("div",{className:"absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-[#13142B]"})]})]},"flakiness-prevention"):n===2?s.jsxs("div",{className:"w-full h-full rounded-[26px] p-8 flex flex-col overflow-hidden animate-fade-in-up",style:{backgroundColor:"#13142B"},children:[s.jsx("h3",{className:"text-2xl font-normal tracking-wide lowercase text-white mb-3",children:"your team can add & adapt test cases"}),s.jsx("p",{className:"text-sm text-octo-text mb-4",children:"You get visual no-code tools that let your team build best-practice tests through recording or prompts - always consistent, well-structured, and easy to understand."}),s.jsxs("div",{className:"relative flex-1 overflow-hidden rounded-lg",children:[s.jsx("img",{src:"/assets/pillars/ai-no-code-tools.png",alt:"AI no-code tools - new test creation",className:"w-full h-full object-cover object-top"}),s.jsx("div",{className:"absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-[#13142B]"})]})]},"ai-no-code-tools"):null})]}),s.jsx("p",{className:"text-center mt-4 text-xs text-octo-text animate-fade-in-up",children:"SOC-2 compliant · integrates with GitHub and Azure Devops · connects with Testrail & XRay · MCP ready"})]})}),s.jsx(Ol,{}),s.jsx(r8,{}),s.jsx(l8,{}),s.jsx("section",{className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsx("h2",{className:"text-center text-4xl md:text-5xl font-black text-white mb-12",children:"full transparency, full control over your tests"}),s.jsxs("div",{className:"grid md:grid-cols-3 gap-6",children:[s.jsxs("a",{href:"https://trust.octomind.dev/",target:"_blank",rel:"noopener noreferrer",className:"bg-octo-card/60 border border-white/10 rounded-2xl p-8 block cursor-pointer transition-all duration-300 hover:border-octo-teal/50 hover:bg-octo-card/80",children:[s.jsx("div",{className:"mb-6 flex justify-center md:justify-start",children:s.jsx("div",{className:"w-16 h-16 rounded-full border-2 border-white/40 flex items-center justify-center",children:s.jsxs("div",{className:"text-center text-[10px] text-white/80 leading-tight",children:[s.jsx("div",{className:"font-semibold",children:"AICPA"}),s.jsx("div",{className:"border-t border-white/40 mt-0.5 pt-0.5 font-bold",children:"SOC-2"})]})})}),s.jsx("h3",{className:"text-xl font-bold text-octo-teal mb-3",children:"privacy and security"}),s.jsx("p",{className:"text-sm text-octo-text",children:"We do not train on your data. We are SOC-2 compliant. You can test private apps behind firewalls."})]}),s.jsxs("a",{href:"https://github.com/OctoMind-dev",target:"_blank",rel:"noopener noreferrer",className:"bg-octo-card/60 border border-white/10 rounded-2xl p-8 block cursor-pointer transition-all duration-300 hover:border-octo-teal/50 hover:bg-octo-card/80",children:[s.jsx("div",{className:"mb-6 flex justify-center md:justify-start",children:s.jsx(K4,{className:"w-16 h-16 text-white",strokeWidth:1.5})}),s.jsx("h3",{className:"text-xl font-bold text-octo-teal mb-3",children:"open source on your machine"}),s.jsx("p",{className:"text-sm text-octo-text",children:"Everything that runs locally is fully open source. Review the code on GitHub anytime and verify exactly what's happening."})]}),s.jsxs("div",{className:"bg-octo-card/60 border border-white/10 rounded-2xl p-8 cursor-pointer transition-all duration-300 hover:border-octo-teal/50 hover:bg-octo-card/80",onClick:()=>t(!0),children:[s.jsx("div",{className:"mb-6 flex justify-center md:justify-start",children:s.jsx(Q4,{className:"w-16 h-16 text-white",strokeWidth:1.5})}),s.jsx("h3",{className:"text-xl font-bold text-octo-teal mb-3",children:"the code is yours"}),s.jsx("p",{className:"text-sm text-octo-text",children:"All test code is standard Playwright and fully portable. You can access it at any time. We don't lock you in."})]}),s.jsx(uk,{open:e,onOpenChange:t,children:s.jsxs(Pf,{className:"bg-transparent border-none max-w-4xl w-[95vw] p-0",children:[s.jsx(mk,{className:"sr-only",children:s.jsx(pk,{children:"Test Code Example"})}),s.jsx("img",{src:"/assets/code-view.png",alt:"Playwright test code example showing login test",className:"w-full h-auto rounded-lg"})]})})]})]})}),s.jsx("section",{id:"faq",className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"mb-6 text-4xl md:text-5xl font-black text-white",children:"frequently asked questions"}),s.jsx("p",{className:"text-xl font-normal text-white",children:"Everything technical leaders need to know"})]}),s.jsx("div",{className:"space-y-4",children:d.map((u,h)=>s.jsxs("details",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden hover:border-white/20 transition-all duration-300",children:[s.jsxs("summary",{className:"w-full px-6 md:px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden",children:[s.jsx("span",{className:"pr-8 text-sm font-normal text-white",children:u.question}),s.jsx(Al,{className:"w-6 h-6 text-octo-purple flex-shrink-0 transition-transform duration-300 group-open:rotate-180",strokeWidth:2})]}),s.jsx("div",{className:"px-6 md:px-8 pb-6 leading-relaxed text-sm font-normal text-white",children:u.answer})]},h))})]})}),s.jsx(se,{})]})}const v8=[{name:"Jasper Masemann",title:"partner, Cherry Ventures",linkedin:"https://www.linkedin.com/in/jmasemann/",image:"/assets/investors/jasper_Masemann.jpeg"},{name:"Sean Mullaney",title:"CTO, Algolia",linkedin:"https://www.linkedin.com/in/seanmullaney/",image:"/assets/investors/sean-mullaney.jpeg"},{name:"Lutz Finger",title:"angel investor, ex-Linkedin, ex-Snap",linkedin:"https://www.linkedin.com/in/lutzfinger/",image:"/assets/investors/Lutz-Finger.webp"},{name:"Alan Hamlett",title:"founder & CEO, WakaTime",linkedin:"https://www.linkedin.com/in/alanhamlett/",image:"/assets/investors/alan-hamlett.jpeg"},{name:"Elias Schneider",title:"co-founder & CEO, Codesphere",linkedin:"https://www.linkedin.com/in/eliasschndr/",image:"/assets/investors/elias-schneider.jpeg"},{name:"Prash Somaiya",title:"CTO, Hadrian.io",linkedin:"https://www.linkedin.com/in/prash7/",image:"/assets/investors/prash-somaiya.jpeg"},{name:"Alen Cvisic",title:"co-founder & CEO, Tola",linkedin:"https://www.linkedin.com/in/alencvisic/",image:"/assets/investors/alen-cvisic.jpeg"},{name:"Cristobal Viedma",title:"founder & CEO, Lingokids",linkedin:"https://www.linkedin.com/in/viedma/",image:"/assets/investors/cristobal-viedma.jpeg"},{name:"Christian Stiebner",title:"angel investor",linkedin:"https://www.linkedin.com/in/christianstiebner/",image:"/assets/investors/christian-stiebner.jpeg"},{name:"Fatima Yusuf",title:"angel investor, ex-Shopify",linkedin:"https://www.linkedin.com/in/fatima-y-2751662a/",image:"/assets/investors/fatima-yusuf.jpeg"},{name:"Philipp Langnickel",title:"CIO, TKM",linkedin:"https://www.linkedin.com/in/philipp-langnickel/",image:"/assets/investors/philipp-langickel.jpeg"},{name:"Charlie Songhurst",title:"angel investor, ex-Microsoft",linkedin:"https://www.linkedin.com/in/charlessonghurst/",image:"/assets/investors/charlie-songhurst.jpeg"},{name:"Tim Suchanek",title:"co-founder, Stellate",linkedin:"https://www.linkedin.com/in/tim-suchanek-08219346/",image:"/assets/investors/tim-suchanek.jpeg"},{name:"Reetu Kainulainen",title:"founder & CEO, Ultimate",linkedin:"https://www.linkedin.com/in/reetukainulainen/",image:"/assets/investors/reetu-kainulainen.jpeg"},{name:"Rogier Fischer",title:"co-founder & CEO, Hadrian.io",linkedin:"https://www.linkedin.com/in/rogierfischer/",image:"/assets/investors/rogier-fischer.jpeg"},{name:"Vanesa Ortiz",title:"dev community manager, Sourcegraph",linkedin:"https://www.linkedin.com/in/vanesaortiz/",image:"/assets/investors/vanesa-ortiz.jpeg"},{name:"Henning Lategahn",title:"founder & CEO, Atlatec",linkedin:"https://www.linkedin.com/in/henning-lategahn/",image:"/assets/investors/henning-lategahn.jpeg"},{name:"Sebastian Weiss",title:"VP DACH, HashiCorp",linkedin:"https://www.linkedin.com/in/weisssebastian/",image:"/assets/investors/sebastian-weiss.jpeg"},{name:"Can Gerling",title:"angel investor",linkedin:"https://www.linkedin.com/in/cangerling/",image:"/assets/investors/can-gerling.jpeg"},{name:"Marius Obiegala",title:"angel investor, Combination VC",linkedin:"https://www.linkedin.com/in/marius-obiegala/",image:"/assets/investors/marius-obiegala.jpeg"},{name:"David Meiborg",title:"partner, First Momentum Ventures",linkedin:"https://www.linkedin.com/in/david-meiborg/",image:"/assets/investors/david-meiborg.jpeg"},{name:"Søren Bramer Schmidt",title:"CEO, Prisma",linkedin:"https://www.linkedin.com/in/sorenbs/",image:"/assets/investors/soren-bramer-schmidt.jpeg"},{name:"Haresh Bajaj",title:"VP product growth, Pleo",linkedin:"https://www.linkedin.com/in/haresh0712/",image:"/assets/investors/haresh-bajaj.jpeg"},{name:"Oliver Manojlovic",title:"VP Sales, Personio",linkedin:"https://www.linkedin.com/in/olivermanojlovic/",image:"/assets/investors/oliver-manojlovic.jpeg"},{name:"Bjarke Klinge Staun",title:"angel investor, ex-Creandum",linkedin:"https://www.linkedin.com/in/bjarkestaun/",image:"/assets/investors/bjarke-klinge-staun.jpeg"},{name:"Christoph Deckert",title:"strategy, Personio",linkedin:"https://www.linkedin.com/in/christophdeckert/",image:"/assets/investors/christoph-deckert.jpeg"},{name:"Felix W. Schmitt",title:"angel investor, ex-Entrepeneur First",linkedin:"https://www.linkedin.com/in/felix-schmitt/",image:"/assets/investors/felix-schmitt.jpeg"},{name:"Johannis Hatt",title:"angel investor, founder, Productsup",linkedin:"https://www.linkedin.com/in/johannishatt/",image:"/assets/investors/johannis-hatt.jpeg"},{name:"Ha Duong",title:"angel investor",linkedin:"https://www.linkedin.com/in/minhhaduong/",image:"/assets/investors/ha-duong.jpeg"},{name:"Laura Raggl",title:"angel investor, ROI Ventures",linkedin:"https://www.linkedin.com/in/laura-raggl/",image:"/assets/investors/laura-raggl.jpeg"}];function y8(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("section",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto text-center relative z-10",children:s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"mb-6 text-5xl md:text-6xl font-black text-white",children:"we help build better software"}),s.jsx("p",{className:"max-w-2xl mx-auto text-xl font-normal text-octo-text",children:"We are on a mission to make quality software testing accessible to every development team"})]})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto relative z-10",children:s.jsx("div",{className:"grid md:grid-cols-3 gap-8",children:[{title:"we care",desc:"deeply about the code quality and the value our app delivers to our users and to their users."},{title:"we believe",desc:"that only end-to-end tests can give you the confidence to ship a product your users will love."},{title:"we are on a mission",desc:"to eliminate the annoying parts of end-to-end testing and speed you up."}].map((e,t)=>s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-6 md:p-8 animate-fade-in-up",children:[s.jsx("h3",{className:"mb-4 text-xl font-black text-white",children:e.title}),s.jsx("p",{className:"text-sm font-normal text-octo-text leading-relaxed",children:e.desc})]},t))})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto relative z-10",children:s.jsxs("div",{className:"text-center mb-12 animate-fade-in-up",children:[s.jsx("h2",{className:"mb-12 text-4xl font-black text-white",children:"backed by incredible investors"}),s.jsxs("div",{className:"bg-white border border-white/10 rounded-2xl p-6 md:p-8 flex items-center justify-center gap-8 animate-fade-in-up",children:[s.jsx("img",{src:"/assets/investors/c8f10f86a_Cherry_Ventures_Logo.png",alt:"Cherry Ventures",className:"h-16 md:h-20 object-contain flex-shrink-0"}),s.jsx("p",{className:"text-xl font-bold text-octo-dark",children:"seed round led by cherry ventures"})]}),s.jsx("div",{className:"grid md:grid-cols-3 gap-6 mt-16 animate-fade-in-up",children:v8.map((e,t)=>s.jsxs("a",{href:e.linkedin,target:"_blank",rel:"noopener noreferrer",className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-3 flex items-center gap-3 hover:border-white/30 hover:bg-octo-card/80 transition-all cursor-pointer animate-fade-in-up",children:[e.image?s.jsx("img",{src:e.image,alt:e.name,className:"w-12 h-12 rounded-full object-cover border-2 border-white/10 flex-shrink-0"}):s.jsx("div",{className:"w-12 h-12 rounded-full bg-octo-purple/10 border-2 border-white/10 flex items-center justify-center flex-shrink-0",children:s.jsx(i6,{className:"w-6 h-6 text-octo-purple"})}),s.jsxs("div",{className:"flex-1 min-w-0 text-left",children:[s.jsx("p",{className:"text-base font-bold text-white truncate",children:e.name}),s.jsx("p",{className:"text-xs font-normal text-octo-text mt-0.5",children:e.title})]})]},t))})]})})}),s.jsxs("section",{className:"py-16 md:py-24 px-4 md:px-6 relative overflow-hidden",children:[s.jsx("div",{className:"absolute inset-0 bg-gradient-to-r from-octo-purple/10 to-octo-blue/10"}),s.jsxs("div",{className:"relative max-w-6xl mx-auto animate-fade-in-up",children:[s.jsx("h2",{className:"mb-12 text-center text-4xl font-black text-white",children:"the founders' story"}),s.jsxs("div",{className:"grid md:grid-cols-2 gap-8 items-center",children:[s.jsx("div",{children:s.jsx("img",{src:"/assets/investors/910cf9807_founders_landscape_3.jpg",alt:"Marc Mengler and Daniel Roedler, founders of Octomind",className:"w-full rounded-2xl"})}),s.jsxs("div",{className:"space-y-4 text-base font-normal text-white leading-relaxed",children:[s.jsx("p",{children:"Octomind was born from Marc Mengler and Daniel Roedler's shared frustration with slow, manual QA. Marc had been building ML-powered web apps since 2015, and Daniel had run GoTo Meeting's massively scaled web client. They met at Marc's startup, Understand.ai, where testing bottlenecks kept draining time and money."}),s.jsx("p",{children:"When genAI breakthroughs hit in early 2023, the solution clicked: combine Daniel's product vision with Marc's ML chops to finally automate the painful parts of end-to-end testing. Octomind is the result."})]})]})]})]}),s.jsx("section",{className:"py-16 md:py-24 px-4 md:px-6 relative overflow-hidden bg-[#0f1020]",children:s.jsx("div",{className:"relative max-w-4xl mx-auto animate-fade-in-up",children:s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-8 md:p-12",children:[s.jsx("h2",{className:"mb-6 text-center text-4xl font-black text-white",children:"future founders promise"}),s.jsxs("div",{className:"space-y-4 text-base font-normal text-white leading-relaxed",children:[s.jsx("p",{children:"At Octomind, we not only recognize the entrepreneurial spirit in our team but actively support it. Our Future Founders Promise is not just about mentorship and encouragement; it's a concrete commitment."}),s.jsx("p",{children:"If you, as a part of our team, decide to embark on your own tech startup journey, we promise to be your first angel investors. This commitment comes from our belief in nurturing talent and innovation."}),s.jsx("p",{children:"We provide not just guidance and a conducive environment, but also the crucial initial funding to help your vision take flight."})]})]})})}),s.jsx(se,{})]})}var S=typeof window<"u"?window:void 0,xt=typeof globalThis<"u"?globalThis:S;typeof self>"u"&&(xt.self=xt),typeof File>"u"&&(xt.File=function(){});var xk=Array.prototype,Ty=xk.forEach,Iy=xk.indexOf,as=xt==null?void 0:xt.navigator,z=xt==null?void 0:xt.document,qt=xt==null?void 0:xt.location,Np=xt==null?void 0:xt.fetch,kp=xt!=null&&xt.XMLHttpRequest&&"withCredentials"in new xt.XMLHttpRequest?xt.XMLHttpRequest:void 0,Ay=xt==null?void 0:xt.AbortController,Bt=as==null?void 0:as.userAgent,ne=S??{},vn={DEBUG:!1,LIB_VERSION:"1.302.2"};function Py(e,t,n,r,o,a,i){try{var l=e[a](i),c=l.value}catch(d){return void n(d)}l.done?t(c):Promise.resolve(c).then(r,o)}function Oy(e){return function(){var t=this,n=arguments;return new Promise(function(r,o){var a=e.apply(t,n);function i(c){Py(a,r,o,i,l,"next",c)}function l(c){Py(a,r,o,i,l,"throw",c)}i(void 0)})}}function G(){return G=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var o=r.toLowerCase();return n.indexOf(o)!==-1})},w8=["$snapshot","$pageview","$pageleave","$set","survey dismissed","survey sent","survey shown","$identify","$groupidentify","$create_alias","$$client_ingestion_warning","$web_experiment_applied","$feature_enrollment_update","$feature_flag_called"];function me(e,t){return e.indexOf(t)!==-1}var $u=function(e){return e.trim()},_p=function(e){return e.replace(/^\$/,"")},j8=Array.isArray,gk=Object.prototype,vk=gk.hasOwnProperty,Lu=gk.toString,Ae=j8||function(e){return Lu.call(e)==="[object Array]"},Nr=e=>typeof e=="function",ct=e=>e===Object(e)&&!Ae(e),Go=e=>{if(ct(e)){for(var t in e)if(vk.call(e,t))return!1;return!0}return!1},L=e=>e===void 0,ot=e=>Lu.call(e)=="[object String]",Cp=e=>ot(e)&&e.trim().length===0,zn=e=>e===null,Ie=e=>L(e)||zn(e),un=e=>Lu.call(e)=="[object Number]",kr=e=>Lu.call(e)==="[object Boolean]",N8=e=>e instanceof FormData,k8=e=>me(w8,e);function Sp(e){return e===null||typeof e!="object"}function Ad(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function yk(e){return!L(Event)&&function(t,n){try{return t instanceof n}catch{return!1}}(e,Event)}var _8=[!0,"true",1,"1","yes"],zh=e=>me(_8,e),C8=[!1,"false",0,"0","no"];function nn(e,t,n,r,o){return t>n&&(r.warn("min cannot be greater than max."),t=n),un(e)?e>n?(r.warn(" cannot be greater than max: "+n+". Using max value instead."),n):e0){var a=o*this.m;t.tokens=Math.min(t.tokens+a,this.o),t.lastAccess=t.lastAccess+o*this.S}}consumeRateLimit(t){var n,r=Date.now(),o=String(t),a=this.t[o];return a?this.$(a,r):(a={tokens:this.o,lastAccess:r},this.t[o]=a),a.tokens===0||(a.tokens--,a.tokens===0&&((n=this.i)==null||n.call(this,t)),a.tokens===0)}stop(){this.t={}}};var hc,Ry,Bh,E8=e=>e instanceof Error;function T8(e){var t=globalThis._posthogChunkIds;if(t){var n=Object.keys(t);return Bh&&n.length===Ry||(Ry=n.length,Bh=n.reduce((r,o)=>{hc||(hc={});var a=hc[o];if(a)r[a[0]]=a[1];else for(var i=e(o),l=i.length-1;l>=0;l--){var c=i[l],d=c==null?void 0:c.filename,u=t[o];if(d&&u){r[d]=u,hc[o]=[d,u];break}}return r},{})),Bh}}class I8{constructor(t,n,r){r===void 0&&(r=[]),this.coercers=t,this.stackParser=n,this.modifiers=r}buildFromUnknown(t,n){n===void 0&&(n={});var r=n&&n.mechanism||{handled:!0,type:"generic"},o=this.buildCoercingContext(r,n,0).apply(t),a=this.buildParsingContext(),i=this.parseStacktrace(o,a);return{$exception_list:this.convertToExceptionList(i,r),$exception_level:"error"}}modifyFrames(t){var n=this;return Oy(function*(){for(var r of t)r.stacktrace&&r.stacktrace.frames&&Ae(r.stacktrace.frames)&&(r.stacktrace.frames=yield n.applyModifiers(r.stacktrace.frames));return t})()}coerceFallback(t){var n;return{type:"Error",value:"Unknown error",stack:(n=t.syntheticException)==null?void 0:n.stack,synthetic:!0}}parseStacktrace(t,n){var r,o;return t.cause!=null&&(r=this.parseStacktrace(t.cause,n)),t.stack!=""&&t.stack!=null&&(o=this.applyChunkIds(this.stackParser(t.stack,t.synthetic?1:0),n.chunkIdMap)),G({},t,{cause:r,stack:o})}applyChunkIds(t,n){return t.map(r=>(r.filename&&n&&(r.chunk_id=n[r.filename]),r))}applyCoercers(t,n){for(var r of this.coercers)if(r.match(t))return r.coerce(t,n);return this.coerceFallback(n)}applyModifiers(t){var n=this;return Oy(function*(){var r=t;for(var o of n.modifiers)r=yield o(r);return r})()}convertToExceptionList(t,n){var r,o,a,i={type:t.type,value:t.value,mechanism:{type:(r=n.type)!==null&&r!==void 0?r:"generic",handled:(o=n.handled)===null||o===void 0||o,synthetic:(a=t.synthetic)!==null&&a!==void 0&&a}};t.stack&&(i.stacktrace={type:"raw",frames:t.stack});var l=[i];return t.cause!=null&&l.push(...this.convertToExceptionList(t.cause,G({},n,{handled:!0}))),l}buildParsingContext(){return{chunkIdMap:T8(this.stackParser)}}buildCoercingContext(t,n,r){r===void 0&&(r=0);var o=(a,i)=>{if(i<=4){var l=this.buildCoercingContext(t,n,i);return this.applyCoercers(a,l)}};return G({},n,{syntheticException:r==0?n.syntheticException:void 0,mechanism:t,apply:a=>o(a,r),next:a=>o(a,r+1)})}}var Sa="?";function Ep(e,t,n,r,o){var a={platform:e,filename:t,function:n===""?Sa:n,in_app:!0};return L(r)||(a.lineno=r),L(o)||(a.colno=o),a}var bk=(e,t)=>{var n=e.indexOf("safari-extension")!==-1,r=e.indexOf("safari-web-extension")!==-1;return n||r?[e.indexOf("@")!==-1?e.split("@")[0]:Sa,n?"safari-extension:"+t:"safari-web-extension:"+t]:[e,t]},A8=/^\s*at (\S+?)(?::(\d+))(?::(\d+))\s*$/i,P8=/^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,O8=/\((\S*)(?::(\d+))(?::(\d+))\)/,D8=(e,t)=>{var n=A8.exec(e);if(n){var[,r,o,a]=n;return Ep(t,r,Sa,+o,+a)}var i=P8.exec(e);if(i){if(i[2]&&i[2].indexOf("eval")===0){var l=O8.exec(i[2]);l&&(i[2]=l[1],i[3]=l[2],i[4]=l[3])}var[c,d]=bk(i[1]||Sa,i[2]);return Ep(t,d,c,i[3]?+i[3]:void 0,i[4]?+i[4]:void 0)}},R8=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i,M8=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,$8=(e,t)=>{var n=R8.exec(e);if(n){if(n[3]&&n[3].indexOf(" > eval")>-1){var r=M8.exec(n[3]);r&&(n[1]=n[1]||"eval",n[3]=r[1],n[4]=r[2],n[5]="")}var o=n[3],a=n[1]||Sa;return[a,o]=bk(a,o),Ep(t,o,a,n[4]?+n[4]:void 0,n[5]?+n[5]:void 0)}},My=/\(error: (.*)\)/,$y=50;function L8(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r1024)){var u=My.test(d)?d.replace(My,"$1"):d;if(!u.match(/\S*Error: /)){for(var h of n){var p=h(u,e);if(p){i.push(p);break}}if(i.length>=$y)break}}}return function(m){if(!m.length)return[];var w=Array.from(m);return w.reverse(),w.slice(0,$y).map(x=>{return G({},x,{filename:x.filename||(b=w,b[b.length-1]||{}).filename,function:x.function||Sa});var b})}(i)}}class F8{match(t){return this.isDOMException(t)||this.isDOMError(t)}coerce(t,n){var r=ot(t.stack);return{type:this.getType(t),value:this.getValue(t),stack:r?t.stack:void 0,cause:t.cause?n.next(t.cause):void 0,synthetic:!1}}getType(t){return this.isDOMError(t)?"DOMError":"DOMException"}getValue(t){var n=t.name||(this.isDOMError(t)?"DOMError":"DOMException");return t.message?n+": "+t.message:n}isDOMException(t){return Ad(t,"DOMException")}isDOMError(t){return Ad(t,"DOMError")}}class z8{match(t){return(n=>n instanceof Error)(t)}coerce(t,n){return{type:this.getType(t),value:this.getMessage(t,n),stack:this.getStack(t),cause:t.cause?n.next(t.cause):void 0,synthetic:!1}}getType(t){return t.name||t.constructor.name}getMessage(t,n){var r=t.message;return r.error&&typeof r.error.message=="string"?String(r.error.message):String(r)}getStack(t){return t.stacktrace||t.stack||void 0}}class B8{constructor(){}match(t){return Ad(t,"ErrorEvent")&&t.error!=null}coerce(t,n){var r,o=n.apply(t.error);return o||{type:"ErrorEvent",value:t.message,stack:(r=n.syntheticException)==null?void 0:r.stack,synthetic:!0}}}var W8=/^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/i;class U8{match(t){return typeof t=="string"}coerce(t,n){var r,[o,a]=this.getInfos(t);return{type:o??"Error",value:a??t,stack:(r=n.syntheticException)==null?void 0:r.stack,synthetic:!0}}getInfos(t){var n="Error",r=t,o=t.match(W8);return o&&(n=o[1],r=o[2]),[n,r]}}var H8=["fatal","error","warning","log","info","debug"];function wk(e,t){t===void 0&&(t=40);var n=Object.keys(e);if(n.sort(),!n.length)return"[object has no keys]";for(var r=n.length;r>0;r--){var o=n.slice(0,r).join(", ");if(!(o.length>t))return r===n.length||o.length<=t?o:o.slice(0,t)+"..."}return""}class q8{match(t){return typeof t=="object"&&t!==null}coerce(t,n){var r,o=this.getErrorPropertyFromObject(t);return o?n.apply(o):{type:this.getType(t),value:this.getValue(t),stack:(r=n.syntheticException)==null?void 0:r.stack,level:this.isSeverityLevel(t.level)?t.level:"error",synthetic:!0}}getType(t){return yk(t)?t.constructor.name:"Error"}getValue(t){if("name"in t&&typeof t.name=="string"){var n="'"+t.name+"' captured as exception";return"message"in t&&typeof t.message=="string"&&(n+=" with message: '"+t.message+"'"),n}if("message"in t&&typeof t.message=="string")return t.message;var r=this.getObjectClassName(t);return(r&&r!=="Object"?"'"+r+"'":"Object")+" captured as exception with keys: "+wk(t)}isSeverityLevel(t){return ot(t)&&!Cp(t)&&H8.indexOf(t)>=0}getErrorPropertyFromObject(t){for(var n in t)if(Object.prototype.hasOwnProperty.call(t,n)){var r=t[n];if(E8(r))return r}}getObjectClassName(t){try{var n=Object.getPrototypeOf(t);return n?n.constructor.name:void 0}catch{return}}}class V8{match(t){return yk(t)}coerce(t,n){var r,o=t.constructor.name;return{type:o,value:o+" captured as exception with keys: "+wk(t),stack:(r=n.syntheticException)==null?void 0:r.stack,synthetic:!0}}}class G8{match(t){return Sp(t)}coerce(t,n){var r;return{type:"Error",value:"Primitive value captured as exception: "+String(t),stack:(r=n.syntheticException)==null?void 0:r.stack,synthetic:!0}}}class Y8{match(t){return Ad(t,"PromiseRejectionEvent")}coerce(t,n){var r,o=this.getUnhandledRejectionReason(t);return Sp(o)?{type:"UnhandledRejection",value:"Non-Error promise rejection captured with value: "+String(o),stack:(r=n.syntheticException)==null?void 0:r.stack,synthetic:!0}:n.apply(o)}getUnhandledRejectionReason(t){if(Sp(t))return t;try{if("reason"in t)return t.reason;if("detail"in t&&"reason"in t.detail)return t.detail.reason}catch{}return t}}var jk=function(e,t){var{debugEnabled:n}=t===void 0?{}:t,r={k:function(o){if(S&&(vn.DEBUG||ne.POSTHOG_DEBUG||n)&&!L(S.console)&&S.console){for(var a=("__rrweb_original__"in S.console[o])?S.console[o].__rrweb_original__:S.console[o],i=arguments.length,l=new Array(i>1?i-1:0),c=1;c{r.error("You must initialize PostHog before calling "+o)},createLogger:(o,a)=>jk(e+" "+o,a)};return r},U=jk("[PostHog.js]"),it=U.createLogger,Q8=it("[ExternalScriptsLoader]"),Ly=(e,t,n)=>{if(e.config.disable_external_dependency_loading)return Q8.warn(t+" was requested but loading of external scripts is disabled."),n("Loading of external scripts is disabled");var r=z==null?void 0:z.querySelectorAll("script");if(r){for(var o,a=function(){if(r[i].src===t){var c=r[i];return c.__posthog_loading_callback_fired?{v:n()}:(c.addEventListener("load",d=>{c.__posthog_loading_callback_fired=!0,n(void 0,d)}),c.onerror=d=>n(d),{v:void 0})}},i=0;i{if(!z)return n("document not found");var c=z.createElement("script");if(c.type="text/javascript",c.crossOrigin="anonymous",c.src=t,c.onload=h=>{c.__posthog_loading_callback_fired=!0,n(void 0,h)},c.onerror=h=>n(h),e.config.prepare_external_dependency_script&&(c=e.config.prepare_external_dependency_script(c)),!c)return n("prepare_external_dependency_script returned null");var d,u=z.querySelectorAll("body > script");u.length>0?(d=u[0].parentNode)==null||d.insertBefore(c,u[0]):z.body.appendChild(c)};z!=null&&z.body?l():z==null||z.addEventListener("DOMContentLoaded",l)};ne.__PosthogExtensions__=ne.__PosthogExtensions__||{},ne.__PosthogExtensions__.loadExternalDependency=(e,t,n)=>{var r="/static/"+t+".js?v="+e.version;if(t==="remote-config"&&(r="/array/"+e.config.token+"/config.js"),t==="toolbar"){var o=3e5;r=r+"&t="+Math.floor(Date.now()/o)*o}var a=e.requestRouter.endpointFor("assets",r);Ly(e,a,n)},ne.__PosthogExtensions__.loadSiteApp=(e,t,n)=>{var r=e.requestRouter.endpointFor("api",t);Ly(e,r,n)};var Pd={};function _r(e,t,n){if(Ae(e)){if(Ty&&e.forEach===Ty)e.forEach(t,n);else if("length"in e&&e.length===+e.length){for(var r=0,o=e.length;r1?t-1:0),r=1;r1?t-1:0),r=1;r0||un(n))&&(t[r]=n)}),t};function X8(e,t){return n=e,r=a=>ot(a)&&!zn(t)?a.slice(0,t):a,o=new Set,function a(i,l){return i!==Object(i)?r?r(i,l):i:o.has(i)?void 0:(o.add(i),Ae(i)?(c=[],_r(i,d=>{c.push(a(d))})):(c={},De(i,(d,u)=>{o.has(d)||(c[u]=a(d,u))})),c);var c}(n);var n,r,o}var J8=["herokuapp.com","vercel.app","netlify.app"];function Z8(e){var t=e==null?void 0:e.hostname;if(!ot(t))return!1;var n=t.split(".").slice(-2).join(".");for(var r of J8)if(n===r)return!1;return!0}function Nk(e,t){for(var n=0;nt.match(n)))}function $d(e){var t="";switch(typeof e.className){case"string":t=e.className;break;case"object":t=(e.className&&"baseVal"in e.className?e.className.baseVal:null)||e.getAttribute("class")||"";break;default:t=""}return Df(t)}function Ik(e){return Ie(e)?null:$u(e).split(/(\s+)/).filter(t=>ml(t)).join("").replace(/[\r\n]/g," ").replace(/[ ]+/g," ").substring(0,255)}function Dl(e){var t="";return $p(e)&&!Rk(e)&&e.childNodes&&e.childNodes.length&&De(e.childNodes,function(n){var r;Ek(n)&&n.textContent&&(t+=(r=Ik(n.textContent))!==null&&r!==void 0?r:"")}),$u(t)}function Ak(e){return L(e.target)?e.srcElement||null:(t=e.target)!=null&&t.shadowRoot?e.composedPath()[0]||null:e.target||null;var t}var Rf=["a","button","form","input","select","textarea","label"];function Pk(e,t){if(L(t))return!0;var n,r=function(a){if(t.some(i=>a.matches(i)))return{v:!0}};for(var o of e)if(n=r(o))return n.v;return!1}function Ok(e){var t=e.parentNode;return!(!t||!Fu(t))&&t}var t5=["next","previous","prev",">","<"],Gy=10,Yy=[".ph-no-rageclick",".ph-no-capture"];function s5(e,t){if(!S||Mf(e))return!1;var n,r,o;if(kr(t)?(n=!!t&&Yy,r=void 0):(n=(o=t==null?void 0:t.css_selector_ignorelist)!==null&&o!==void 0?o:Yy,r=t==null?void 0:t.content_ignorelist),n===!1)return!1;var{targetElementList:a}=Dk(e,!1);return!function(i,l){if(i===!1||L(i))return!1;var c;if(i===!0)c=t5;else{if(!Ae(i))return!1;if(i.length>Gy)return U.error("[PostHog] content_ignorelist array cannot exceed "+Gy+" items. Use css_selector_ignorelist for more complex matching."),!1;c=i.map(d=>d.toLowerCase())}return l.some(d=>{var{safeText:u,ariaLabel:h}=d;return c.some(p=>u.includes(p)||h.includes(p))})}(r,a.map(i=>{var l;return{safeText:Dl(i).toLowerCase(),ariaLabel:((l=i.getAttribute("aria-label"))==null?void 0:l.toLowerCase().trim())||""}}))&&!Pk(a,n)}var Mf=e=>!e||Cr(e,"html")||!Fu(e),Dk=(e,t)=>{if(!S||Mf(e))return{parentIsUsefulElement:!1,targetElementList:[]};for(var n=!1,r=[e],o=e;o.parentNode&&!Cr(o,"body");)if(Tk(o.parentNode))r.push(o.parentNode.host),o=o.parentNode.host;else{var a=Ok(o);if(!a)break;if(t||Rf.indexOf(a.tagName.toLowerCase())>-1)n=!0;else{var i=S.getComputedStyle(a);i&&i.getPropertyValue("cursor")==="pointer"&&(n=!0)}r.push(a),o=a}return{parentIsUsefulElement:n,targetElementList:r}};function n5(e,t,n,r,o){var a,i,l,c;if(n===void 0&&(n=void 0),!S||Mf(e)||(a=n)!=null&&a.url_allowlist&&!Vy(n.url_allowlist)||(i=n)!=null&&i.url_ignorelist&&Vy(n.url_ignorelist))return!1;if((l=n)!=null&&l.dom_event_allowlist){var d=n.dom_event_allowlist;if(d&&!d.some(w=>t.type===w))return!1}var{parentIsUsefulElement:u,targetElementList:h}=Dk(e,r);if(!function(w,x){var b=x==null?void 0:x.element_allowlist;if(L(b))return!0;var v,g=function(j){if(b.some(N=>j.tagName.toLowerCase()===N))return{v:!0}};for(var y of w)if(v=g(y))return v.v;return!1}(h,n)||!Pk(h,(c=n)==null?void 0:c.css_selector_allowlist))return!1;var p=S.getComputedStyle(e);if(p&&p.getPropertyValue("cursor")==="pointer"&&t.type==="click")return!0;var m=e.tagName.toLowerCase();switch(m){case"html":return!1;case"form":return(o||["submit"]).indexOf(t.type)>=0;case"input":case"select":case"textarea":return(o||["change","click"]).indexOf(t.type)>=0;default:return u?(o||["click"]).indexOf(t.type)>=0:(o||["click"]).indexOf(t.type)>=0&&(Rf.indexOf(m)>-1||e.getAttribute("contenteditable")==="true")}}function $p(e){for(var t=e;t.parentNode&&!Cr(t,"body");t=t.parentNode){var n=$d(t);if(me(n,"ph-sensitive")||me(n,"ph-no-capture"))return!1}if(me($d(e),"ph-include"))return!0;var r=e.type||"";if(ot(r))switch(r.toLowerCase()){case"hidden":case"password":return!1}var o=e.name||e.id||"";return!(ot(o)&&/^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i.test(o.replace(/[^a-zA-Z0-9]/g,"")))}function Rk(e){return!!(Cr(e,"input")&&!["button","checkbox","submit","reset"].includes(e.type)||Cr(e,"select")||Cr(e,"textarea")||e.getAttribute("contenteditable")==="true")}var Mk="(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})",r5=new RegExp("^(?:"+Mk+")$"),o5=new RegExp(Mk),$k="\\d{3}-?\\d{2}-?\\d{4}",a5=new RegExp("^("+$k+")$"),i5=new RegExp("("+$k+")");function ml(e,t){return t===void 0&&(t=!0),!(Ie(e)||ot(e)&&(e=$u(e),(t?r5:o5).test((e||"").replace(/[- ]/g,""))||(t?a5:i5).test(e)))}function Lk(e){var t=Dl(e);return ml(t=(t+" "+Fk(e)).trim())?t:""}function Fk(e){var t="";return e&&e.childNodes&&e.childNodes.length&&De(e.childNodes,function(n){var r;if(n&&((r=n.tagName)==null?void 0:r.toLowerCase())==="span")try{var o=Dl(n);t=(t+" "+o).trim(),n.childNodes&&n.childNodes.length&&(t=(t+" "+Fk(n)).trim())}catch(a){U.error("[AutoCapture]",a)}}),t}function l5(e){return function(t){var n=t.map(r=>{var o,a,i="";if(r.tag_name&&(i+=r.tag_name),r.attr_class)for(var l of(r.attr_class.sort(),r.attr_class))i+="."+l.replace(/"/g,"");var c=G({},r.text?{text:r.text}:{},{"nth-child":(o=r.nth_child)!==null&&o!==void 0?o:0,"nth-of-type":(a=r.nth_of_type)!==null&&a!==void 0?a:0},r.href?{href:r.href}:{},r.attr_id?{attr_id:r.attr_id}:{},r.attributes),d={};return zc(c).sort((u,h)=>{var[p]=u,[m]=h;return p.localeCompare(m)}).forEach(u=>{var[h,p]=u;return d[Qy(h.toString())]=Qy(p.toString())}),i+=":",i+=zc(d).map(u=>{var[h,p]=u;return h+'="'+p+'"'}).join("")});return n.join(";")}(function(t){return t.map(n=>{var r,o,a={text:(r=n.$el_text)==null?void 0:r.slice(0,400),tag_name:n.tag_name,href:(o=n.attr__href)==null?void 0:o.slice(0,2048),attr_class:c5(n),attr_id:n.attr__id,nth_child:n.nth_child,nth_of_type:n.nth_of_type,attributes:{}};return zc(n).filter(i=>{var[l]=i;return l.indexOf("attr__")===0}).forEach(i=>{var[l,c]=i;return a.attributes[l]=c}),a})}(e))}function Qy(e){return e.replace(/"|\\"/g,'\\"')}function c5(e){var t=e.attr__class;return t?Ae(t)?t:Df(t):void 0}class zk{constructor(t){this.disabled=t===!1;var n=ct(t)?t:{};this.thresholdPx=n.threshold_px||30,this.timeoutMs=n.timeout_ms||1e3,this.clickCount=n.click_count||3,this.clicks=[]}isRageClick(t,n,r){if(this.disabled)return!1;var o=this.clicks[this.clicks.length-1];if(o&&Math.abs(t-o.x)+Math.abs(n-o.y){var t=z==null?void 0:z.createElement("a");return L(t)?null:(t.href=e,t)},d5=function(e,t){var n,r;t===void 0&&(t="&");var o=[];return De(e,function(a,i){L(a)||L(i)||i==="undefined"||(n=encodeURIComponent((l=>l instanceof File)(a)?a.name:a.toString()),r=encodeURIComponent(i),o[o.length]=r+"="+n)}),o.join(t)},Fd=function(e,t){for(var n,r=((e.split("#")[0]||"").split(/\?(.*)/)[1]||"").replace(/^\?+/g,"").split("&"),o=0;oe?t.slice(0,e)+"...":t}function u5(e){if(e.previousElementSibling)return e.previousElementSibling;var t=e;do t=t.previousSibling;while(t&&!Fu(t));return t}function h5(e,t,n,r){var o=e.tagName.toLowerCase(),a={tag_name:o};Rf.indexOf(o)>-1&&!n&&(o.toLowerCase()==="a"||o.toLowerCase()==="button"?a.$el_text=Hh(1024,Lk(e)):a.$el_text=Hh(1024,Dl(e)));var i=$d(e);i.length>0&&(a.classes=i.filter(function(u){return u!==""})),De(e.attributes,function(u){var h;if((!Rk(e)||["name","id","class","aria-label"].indexOf(u.name)!==-1)&&(r==null||!r.includes(u.name))&&!t&&ml(u.value)&&(h=u.name,!ot(h)||h.substring(0,10)!=="_ngcontent"&&h.substring(0,7)!=="_nghost")){var p=u.value;u.name==="class"&&(p=Df(p).join(" ")),a["attr__"+u.name]=Hh(1024,p)}});for(var l=1,c=1,d=e;d=u5(d);)l++,d.tagName===e.tagName&&c++;return a.nth_child=l,a.nth_of_type=c,a}function m5(e,t){for(var n,r,{e:o,maskAllElementAttributes:a,maskAllText:i,elementAttributeIgnoreList:l,elementsChainAsString:c}=t,d=[e],u=e;u.parentNode&&!Cr(u,"body");)Tk(u.parentNode)?(d.push(u.parentNode.host),u=u.parentNode.host):(d.push(u.parentNode),u=u.parentNode);var h,p=[],m={},w=!1,x=!1;if(De(d,j=>{var N=$p(j);j.tagName.toLowerCase()==="a"&&(w=j.getAttribute("href"),w=N&&w&&ml(w)&&w),me($d(j),"ph-no-capture")&&(x=!0),p.push(h5(j,a,i,l));var _=function(k){if(!$p(k))return{};var C={};return De(k.attributes,function(P){if(P.name&&P.name.indexOf("data-ph-capture-attribute")===0){var A=P.name.replace("data-ph-capture-attribute-",""),H=P.value;A&&H&&ml(H)&&(C[A]=H)}}),C}(j);Je(m,_)}),x)return{props:{},explicitNoCapture:x};if(i||(e.tagName.toLowerCase()==="a"||e.tagName.toLowerCase()==="button"?p[0].$el_text=Lk(e):p[0].$el_text=Dl(e)),w){var b,v;p[0].attr__href=w;var g=(b=Ld(w))==null?void 0:b.host,y=S==null||(v=S.location)==null?void 0:v.host;g&&y&&g!==y&&(h=w)}return{props:Je({$event_type:o.type,$ce_version:1},c?{}:{$elements:p},{$elements_chain:l5(p)},(n=p[0])!=null&&n.$el_text?{$el_text:(r=p[0])==null?void 0:r.$el_text}:{},h&&o.type==="click"?{$external_click_url:h}:{},m)}}class p5{constructor(t){this.P=!1,this.T=null,this.R=!1,this.instance=t,this.rageclicks=new zk(t.config.rageclick),this.I=null}get F(){var t,n,r=ct(this.instance.config.autocapture)?this.instance.config.autocapture:{};return r.url_allowlist=(t=r.url_allowlist)==null?void 0:t.map(o=>new RegExp(o)),r.url_ignorelist=(n=r.url_ignorelist)==null?void 0:n.map(o=>new RegExp(o)),r}C(){if(this.isBrowserSupported()){if(S&&z){var t=r=>{r=r||(S==null?void 0:S.event);try{this.M(r)}catch(o){Ky.error("Failed to capture event",o)}};if(nt(z,"submit",t,{capture:!0}),nt(z,"change",t,{capture:!0}),nt(z,"click",t,{capture:!0}),this.F.capture_copied_text){var n=r=>{r=r||(S==null?void 0:S.event),this.M(r,Uh)};nt(z,"copy",n,{capture:!0}),nt(z,"cut",n,{capture:!0})}}}else Ky.info("Disabling Automatic Event Collection because this browser is not supported")}startIfEnabled(){this.isEnabled&&!this.P&&(this.C(),this.P=!0)}onRemoteConfig(t){t.elementsChainAsString&&(this.R=t.elementsChainAsString),this.instance.persistence&&this.instance.persistence.register({[zy]:!!t.autocapture_opt_out}),this.T=!!t.autocapture_opt_out,this.startIfEnabled()}setElementSelectors(t){this.I=t}getElementSelectors(t){var n,r=[];return(n=this.I)==null||n.forEach(o=>{var a=z==null?void 0:z.querySelectorAll(o);a==null||a.forEach(i=>{t===i&&r.push(o)})}),r}get isEnabled(){var t,n,r=(t=this.instance.persistence)==null?void 0:t.props[zy],o=this.T;if(zn(o)&&!kr(r)&&!this.instance.O())return!1;var a=(n=this.T)!==null&&n!==void 0?n:!!r;return!!this.instance.config.autocapture&&!a}M(t,n){if(n===void 0&&(n="$autocapture"),this.isEnabled){var r,o=Ak(t);Ek(o)&&(o=o.parentNode||null),n==="$autocapture"&&t.type==="click"&&t instanceof MouseEvent&&this.instance.config.rageclick&&(r=this.rageclicks)!=null&&r.isRageClick(t.clientX,t.clientY,t.timeStamp||new Date().getTime())&&s5(o,this.instance.config.rageclick)&&this.M(t,"$rageclick");var a=n===Uh;if(o&&n5(o,t,this.F,a,a?["copy","cut"]:void 0)){var{props:i,explicitNoCapture:l}=m5(o,{e:t,maskAllElementAttributes:this.instance.config.mask_all_element_attributes,maskAllText:this.instance.config.mask_all_text,elementAttributeIgnoreList:this.F.element_attribute_ignorelist,elementsChainAsString:this.R});if(l)return!1;var c=this.getElementSelectors(o);if(c&&c.length>0&&(i.$element_selectors=c),n===Uh){var d,u=Ik(S==null||(d=S.getSelection())==null?void 0:d.toString()),h=t.type||"clipboard";if(!u)return!1;i.$selected_content=u,i.$copy_type=h}return this.instance.capture(n,i),!0}}}isBrowserSupported(){return Nr(z==null?void 0:z.querySelectorAll)}}Math.trunc||(Math.trunc=function(e){return e<0?Math.ceil(e):Math.floor(e)}),Number.isInteger||(Number.isInteger=function(e){return un(e)&&isFinite(e)&&Math.floor(e)===e});var Xy="0123456789abcdef";class Bd{constructor(t){if(this.bytes=t,t.length!==16)throw new TypeError("not 128-bit length")}static fromFieldsV7(t,n,r,o){if(!Number.isInteger(t)||!Number.isInteger(n)||!Number.isInteger(r)||!Number.isInteger(o)||t<0||n<0||r<0||o<0||t>0xffffffffffff||n>4095||r>1073741823||o>4294967295)throw new RangeError("invalid field value");var a=new Uint8Array(16);return a[0]=t/Math.pow(2,40),a[1]=t/Math.pow(2,32),a[2]=t/Math.pow(2,24),a[3]=t/Math.pow(2,16),a[4]=t/Math.pow(2,8),a[5]=t,a[6]=112|n>>>8,a[7]=n,a[8]=128|r>>>24,a[9]=r>>>16,a[10]=r>>>8,a[11]=r,a[12]=o>>>24,a[13]=o>>>16,a[14]=o>>>8,a[15]=o,new Bd(a)}toString(){for(var t="",n=0;n>>4)+Xy.charAt(15&this.bytes[n]),n!==3&&n!==5&&n!==7&&n!==9||(t+="-");if(t.length!==36)throw new Error("Invalid UUIDv7 was generated");return t}clone(){return new Bd(this.bytes.slice(0))}equals(t){return this.compareTo(t)===0}compareTo(t){for(var n=0;n<16;n++){var r=this.bytes[n]-t.bytes[n];if(r!==0)return Math.sign(r)}return 0}}class x5{constructor(){this.A=0,this.D=0,this.j=new f5}generate(){var t=this.generateOrAbort();if(L(t)){this.A=0;var n=this.generateOrAbort();if(L(n))throw new Error("Could not generate UUID after timestamp reset");return n}return t}generateOrAbort(){var t=Date.now();if(t>this.A)this.A=t,this.L();else{if(!(t+1e4>this.A))return;this.D++,this.D>4398046511103&&(this.A++,this.L())}return Bd.fromFieldsV7(this.A,Math.trunc(this.D/Math.pow(2,30)),this.D&Math.pow(2,30)-1,this.j.nextUint32())}L(){this.D=1024*this.j.nextUint32()+(1023&this.j.nextUint32())}}var Jy,Bk=e=>{if(typeof UUIDV7_DENY_WEAK_RNG<"u"&&UUIDV7_DENY_WEAK_RNG)throw new Error("no cryptographically strong RNG available");for(var t=0;tcrypto.getRandomValues(e));class f5{constructor(){this.N=new Uint32Array(8),this.U=1/0}nextUint32(){return this.U>=this.N.length&&(Bk(this.N),this.U=0),this.N[this.U++]}}var ur=()=>g5().toString(),g5=()=>(Jy||(Jy=new x5)).generate(),oi="",v5=/[a-z0-9][a-z0-9-]+\.[a-z]{2,}$/i;function y5(e,t){if(t){var n=function(o,a){if(a===void 0&&(a=z),oi)return oi;if(!a||["localhost","127.0.0.1"].includes(o))return"";for(var i=o.split("."),l=Math.min(i.length,8),c="dmn_chk_"+ur();!oi&&l--;){var d=i.slice(l).join("."),u=c+"=1;domain=."+d+";path=/";a.cookie=u+";max-age=3",a.cookie.includes(c)&&(a.cookie=u+";max-age=0",oi=d)}return oi}(e);if(!n){var r=(o=>{var a=o.match(v5);return a?a[0]:""})(e);r!==n&&U.info("Warning: cookie subdomain discovery mismatch",r,n),n=r}return n?"; domain=."+n:""}return""}var cn={H:()=>!!z,B:function(e){U.error("cookieStore error: "+e)},q:function(e){if(z){try{for(var t=e+"=",n=z.cookie.split(";").filter(a=>a.length),r=0;r3686.4&&U.warn("cookieStore warning: large cookie, len="+d.length),z.cookie=d,d}catch{return}},V:function(e,t){if(z!=null&&z.cookie)try{cn.G(e,"",-1,t)}catch{return}}},qh=null,Ge={H:function(){if(!zn(qh))return qh;var e=!0;if(L(S))e=!1;else try{var t="__mplssupport__";Ge.G(t,"xyz"),Ge.q(t)!=='"xyz"'&&(e=!1),Ge.V(t)}catch{e=!1}return e||U.error("localStorage unsupported; falling back to cookie store"),qh=e,e},B:function(e){U.error("localStorage error: "+e)},q:function(e){try{return S==null?void 0:S.localStorage.getItem(e)}catch(t){Ge.B(t)}return null},W:function(e){try{return JSON.parse(Ge.q(e))||{}}catch{}return null},G:function(e,t){try{S==null||S.localStorage.setItem(e,JSON.stringify(t))}catch(n){Ge.B(n)}},V:function(e){try{S==null||S.localStorage.removeItem(e)}catch(t){Ge.B(t)}}},b5=["distinct_id",Od,Ck,Md,Rd],pc=G({},Ge,{W:function(e){try{var t={};try{t=cn.W(e)||{}}catch{}var n=Je(t,JSON.parse(Ge.q(e)||"{}"));return Ge.G(e,n),n}catch{}return null},G:function(e,t,n,r,o,a){try{Ge.G(e,t,void 0,void 0,a);var i={};b5.forEach(l=>{t[l]&&(i[l]=t[l])}),Object.keys(i).length&&cn.G(e,i,n,r,o,a)}catch(l){Ge.B(l)}},V:function(e,t){try{S==null||S.localStorage.removeItem(e),cn.V(e,t)}catch(n){Ge.B(n)}}}),xc={},w5={H:function(){return!0},B:function(e){U.error("memoryStorage error: "+e)},q:function(e){return xc[e]||null},W:function(e){return xc[e]||null},G:function(e,t){xc[e]=t},V:function(e){delete xc[e]}},Fr=null,mt={H:function(){if(!zn(Fr))return Fr;if(Fr=!0,L(S))Fr=!1;else try{var e="__support__";mt.G(e,"xyz"),mt.q(e)!=='"xyz"'&&(Fr=!1),mt.V(e)}catch{Fr=!1}return Fr},B:function(e){U.error("sessionStorage error: ",e)},q:function(e){try{return S==null?void 0:S.sessionStorage.getItem(e)}catch(t){mt.B(t)}return null},W:function(e){try{return JSON.parse(mt.q(e))||null}catch{}return null},G:function(e,t){try{S==null||S.sessionStorage.setItem(e,JSON.stringify(t))}catch(n){mt.B(n)}},V:function(e){try{S==null||S.sessionStorage.removeItem(e)}catch(t){mt.B(t)}}},bn=function(e){return e[e.PENDING=-1]="PENDING",e[e.DENIED=0]="DENIED",e[e.GRANTED=1]="GRANTED",e}({});class j5{constructor(t){this._instance=t}get F(){return this._instance.config}get consent(){return this.J()?bn.DENIED:this.K}isOptedOut(){return this.F.cookieless_mode==="always"||this.consent===bn.DENIED||this.consent===bn.PENDING&&(this.F.opt_out_capturing_by_default||this.F.cookieless_mode==="on_reject")}isOptedIn(){return!this.isOptedOut()}isExplicitlyOptedOut(){return this.consent===bn.DENIED}optInOut(t){this.Y.G(this.X,t?1:0,this.F.cookie_expiration,this.F.cross_subdomain_cookie,this.F.secure_cookie)}reset(){this.Y.V(this.X,this.F.cross_subdomain_cookie)}get X(){var{token:t,opt_out_capturing_cookie_prefix:n,consent_persistence_name:r}=this._instance.config;return r||(n?n+t:"__ph_opt_in_out_"+t)}get K(){var t=this.Y.q(this.X);return zh(t)?bn.GRANTED:me(C8,t)?bn.DENIED:bn.PENDING}get Y(){if(!this.Z){var t=this.F.opt_out_capturing_persistence_type;this.Z=t==="localStorage"?Ge:cn;var n=t==="localStorage"?cn:Ge;n.q(this.X)&&(this.Z.q(this.X)||this.optInOut(zh(n.q(this.X))),n.V(this.X,this.F.cross_subdomain_cookie))}return this.Z}J(){return!!this.F.respect_dnt&&!!Nk([as==null?void 0:as.doNotTrack,as==null?void 0:as.msDoNotTrack,ne.doNotTrack],t=>zh(t))}}var fc=it("[Dead Clicks]"),N5=()=>!0,k5=e=>{var t,n=!((t=e.instance.persistence)==null||!t.get_property(_k)),r=e.instance.config.capture_dead_clicks;return kr(r)?r:!!ct(r)||n};class Wk{get lazyLoadedDeadClicksAutocapture(){return this.tt}constructor(t,n,r){this.instance=t,this.isEnabled=n,this.onCapture=r,this.startIfEnabled()}onRemoteConfig(t){this.instance.persistence&&this.instance.persistence.register({[_k]:t==null?void 0:t.captureDeadClicks}),this.startIfEnabled()}startIfEnabled(){this.isEnabled(this)&&this.it(()=>{this.et()})}it(t){var n,r;(n=ne.__PosthogExtensions__)!=null&&n.initDeadClicksAutocapture&&t(),(r=ne.__PosthogExtensions__)==null||r.loadExternalDependency==null||r.loadExternalDependency(this.instance,"dead-clicks-autocapture",o=>{o?fc.error("failed to load script",o):t()})}et(){var t;if(z){if(!this.tt&&(t=ne.__PosthogExtensions__)!=null&&t.initDeadClicksAutocapture){var n=ct(this.instance.config.capture_dead_clicks)?this.instance.config.capture_dead_clicks:{};n.__onCapture=this.onCapture,this.tt=ne.__PosthogExtensions__.initDeadClicksAutocapture(this.instance,n),this.tt.start(z),fc.info("starting...")}}else fc.error("`document` not found. Cannot start.")}stop(){this.tt&&(this.tt.stop(),this.tt=void 0,fc.info("stopping..."))}}var ai=it("[ExceptionAutocapture]");class _5{constructor(t){var n,r,o;this.rt=()=>{var a;if(S&&this.isEnabled&&(a=ne.__PosthogExtensions__)!=null&&a.errorWrappingFunctions){var i=ne.__PosthogExtensions__.errorWrappingFunctions.wrapOnError,l=ne.__PosthogExtensions__.errorWrappingFunctions.wrapUnhandledRejection,c=ne.__PosthogExtensions__.errorWrappingFunctions.wrapConsoleError;try{!this.st&&this.F.capture_unhandled_errors&&(this.st=i(this.captureException.bind(this))),!this.nt&&this.F.capture_unhandled_rejections&&(this.nt=l(this.captureException.bind(this))),!this.ot&&this.F.capture_console_errors&&(this.ot=c(this.captureException.bind(this)))}catch(d){ai.error("failed to start",d),this.lt()}}},this._instance=t,this.ut=!((n=this._instance.persistence)==null||!n.props[By]),this.F=this.ht(),this.vt=new S8({refillRate:(r=this._instance.config.error_tracking.__exceptionRateLimiterRefillRate)!==null&&r!==void 0?r:1,bucketSize:(o=this._instance.config.error_tracking.__exceptionRateLimiterBucketSize)!==null&&o!==void 0?o:10,refillInterval:1e4,h:ai}),this.startIfEnabled()}ht(){var t=this._instance.config.capture_exceptions,n={capture_unhandled_errors:!1,capture_unhandled_rejections:!1,capture_console_errors:!1};return ct(t)?n=G({},n,t):(L(t)?this.ut:t)&&(n=G({},n,{capture_unhandled_errors:!0,capture_unhandled_rejections:!0})),n}get isEnabled(){return this.F.capture_console_errors||this.F.capture_unhandled_errors||this.F.capture_unhandled_rejections}startIfEnabled(){this.isEnabled&&(ai.info("enabled"),this.it(this.rt))}it(t){var n,r;(n=ne.__PosthogExtensions__)!=null&&n.errorWrappingFunctions&&t(),(r=ne.__PosthogExtensions__)==null||r.loadExternalDependency==null||r.loadExternalDependency(this._instance,"exception-autocapture",o=>{if(o)return ai.error("failed to load script",o);t()})}lt(){var t,n,r;(t=this.st)==null||t.call(this),this.st=void 0,(n=this.nt)==null||n.call(this),this.nt=void 0,(r=this.ot)==null||r.call(this),this.ot=void 0}onRemoteConfig(t){var n=t.autocaptureExceptions;this.ut=!!n||!1,this.F=this.ht(),this._instance.persistence&&this._instance.persistence.register({[By]:this.ut}),this.startIfEnabled()}captureException(t){var n,r,o=(n=t==null||(r=t.$exception_list)==null||(r=r[0])==null?void 0:r.type)!==null&&n!==void 0?n:"Exception";this.vt.consumeRateLimit(o)?ai.info("Skipping exception capture because of client rate limiting.",{exception:o}):this._instance.exceptions.sendExceptionEvent(t)}}function Zy(e,t,n){try{if(!(t in e))return()=>{};var r=e[t],o=n(r);return Nr(o)&&(o.prototype=o.prototype||{},Object.defineProperties(o,{__posthog_wrapped__:{enumerable:!1,value:!0}})),e[t]=o,()=>{e[t]=r}}catch{return()=>{}}}class C5{constructor(t){var n;this._instance=t,this.dt=(S==null||(n=S.location)==null?void 0:n.pathname)||""}get isEnabled(){return this._instance.config.capture_pageview==="history_change"}startIfEnabled(){this.isEnabled&&(U.info("History API monitoring enabled, starting..."),this.monitorHistoryChanges())}stop(){this.ct&&this.ct(),this.ct=void 0,U.info("History API monitoring stopped")}monitorHistoryChanges(){var t,n;if(S&&S.history){var r=this;(t=S.history.pushState)!=null&&t.__posthog_wrapped__||Zy(S.history,"pushState",o=>function(a,i,l){o.call(this,a,i,l),r.ft("pushState")}),(n=S.history.replaceState)!=null&&n.__posthog_wrapped__||Zy(S.history,"replaceState",o=>function(a,i,l){o.call(this,a,i,l),r.ft("replaceState")}),this.gt()}}ft(t){try{var n,r=S==null||(n=S.location)==null?void 0:n.pathname;if(!r)return;r!==this.dt&&this.isEnabled&&this._instance.capture("$pageview",{navigation_type:t}),this.dt=r}catch(o){U.error("Error capturing "+t+" pageview",o)}}gt(){if(!this.ct){var t=()=>{this.ft("popstate")};nt(S,"popstate",t),this.ct=()=>{S&&S.removeEventListener("popstate",t)}}}}var Vh=it("[SegmentIntegration]");function S5(e,t){var n=e.config.segment;if(!n)return t();(function(r,o){var a=r.config.segment;if(!a)return o();var i=c=>{var d=()=>c.anonymousId()||ur();r.config.get_device_id=d,c.id()&&(r.register({distinct_id:c.id(),$device_id:d()}),r.persistence.set_property(yn,"identified")),o()},l=a.user();"then"in l&&Nr(l.then)?l.then(c=>i(c)):i(l)})(e,()=>{n.register((r=>{Promise&&Promise.resolve||Vh.warn("This browser does not have Promise support, and can not use the segment integration");var o=(a,i)=>{if(!i)return a;a.event.userId||a.event.anonymousId===r.get_distinct_id()||(Vh.info("No userId set, resetting PostHog"),r.reset()),a.event.userId&&a.event.userId!==r.get_distinct_id()&&(Vh.info("UserId set, identifying with PostHog"),r.identify(a.event.userId));var l=r.calculateEventProperties(i,a.event.properties);return a.event.properties=Object.assign({},l,a.event.properties),a};return{name:"PostHog JS",type:"enrichment",version:"1.0.0",isLoaded:()=>!0,load:()=>Promise.resolve(),track:a=>o(a,a.event.event),page:a=>o(a,"$pageview"),identify:a=>o(a,"$identify"),screen:a=>o(a,"$screen")}})(e)).then(()=>{t()})})}var Uk="posthog-js";function Hk(e,t){var{organization:n,projectId:r,prefix:o,severityAllowList:a=["error"],sendExceptionsToPostHog:i=!0}=t===void 0?{}:t;return l=>{var c,d,u,h,p;if(!(a==="*"||a.includes(l.level))||!e.__loaded)return l;l.tags||(l.tags={});var m=e.requestRouter.endpointFor("ui","/project/"+e.config.token+"/person/"+e.get_distinct_id());l.tags["PostHog Person URL"]=m,e.sessionRecordingStarted()&&(l.tags["PostHog Recording URL"]=e.get_session_replay_url({withTimestamp:!0}));var w=((c=l.exception)==null?void 0:c.values)||[],x=w.map(v=>G({},v,{stacktrace:v.stacktrace?G({},v.stacktrace,{type:"raw",frames:(v.stacktrace.frames||[]).map(g=>G({},g,{platform:"web:javascript"}))}):void 0})),b={$exception_message:((d=w[0])==null?void 0:d.value)||l.message,$exception_type:(u=w[0])==null?void 0:u.type,$exception_level:l.level,$exception_list:x,$sentry_event_id:l.event_id,$sentry_exception:l.exception,$sentry_exception_message:((h=w[0])==null?void 0:h.value)||l.message,$sentry_exception_type:(p=w[0])==null?void 0:p.type,$sentry_tags:l.tags};return n&&r&&(b.$sentry_url=(o||"https://sentry.io/organizations/")+n+"/issues/?project="+r+"&query="+l.event_id),i&&e.exceptions.sendExceptionEvent(b),l}}class E5{constructor(t,n,r,o,a,i){this.name=Uk,this.setupOnce=function(l){l(Hk(t,{organization:n,projectId:r,prefix:o,severityAllowList:a,sendExceptionsToPostHog:i==null||i}))}}}var T5=S!=null&&S.location?zd(S.location.hash,"__posthog")||zd(location.hash,"state"):null,eb="_postHogToolbarParams",tb=it("[Toolbar]"),Xn=function(e){return e[e.UNINITIALIZED=0]="UNINITIALIZED",e[e.LOADING=1]="LOADING",e[e.LOADED=2]="LOADED",e}(Xn||{});class I5{constructor(t){this.instance=t}_t(t){ne.ph_toolbar_state=t}yt(){var t;return(t=ne.ph_toolbar_state)!==null&&t!==void 0?t:Xn.UNINITIALIZED}maybeLoadToolbar(t,n,r){if(t===void 0&&(t=void 0),n===void 0&&(n=void 0),r===void 0&&(r=void 0),!S||!z)return!1;t=t??S.location,r=r??S.history;try{if(!n){try{S.localStorage.setItem("test","test"),S.localStorage.removeItem("test")}catch{return!1}n=S==null?void 0:S.localStorage}var o,a=T5||zd(t.hash,"__posthog")||zd(t.hash,"state"),i=a?Fy(()=>JSON.parse(atob(decodeURIComponent(a))))||Fy(()=>JSON.parse(decodeURIComponent(a))):null;return i&&i.action==="ph_authorize"?((o=i).source="url",o&&Object.keys(o).length>0&&(i.desiredHash?t.hash=i.desiredHash:r?r.replaceState(r.state,"",t.pathname+t.search):t.hash="")):((o=JSON.parse(n.getItem(eb)||"{}")).source="localstorage",delete o.userIntent),!(!o.token||this.instance.config.token!==o.token)&&(this.loadToolbar(o),!0)}catch{return!1}}bt(t){var n=ne.ph_load_toolbar||ne.ph_load_editor;!Ie(n)&&Nr(n)?n(t,this.instance):tb.warn("No toolbar load function found")}loadToolbar(t){var n=!(z==null||!z.getElementById(Sk));if(!S||n)return!1;var r=this.instance.requestRouter.region==="custom"&&this.instance.config.advanced_disable_toolbar_metrics,o=G({token:this.instance.config.token},t,{apiURL:this.instance.requestRouter.endpointFor("ui")},r?{instrument:!1}:{});if(S.localStorage.setItem(eb,JSON.stringify(G({},o,{source:void 0}))),this.yt()===Xn.LOADED)this.bt(o);else if(this.yt()===Xn.UNINITIALIZED){var a;this._t(Xn.LOADING),(a=ne.__PosthogExtensions__)==null||a.loadExternalDependency==null||a.loadExternalDependency(this.instance,"toolbar",i=>{if(i)return tb.error("[Toolbar] Failed to load",i),void this._t(Xn.UNINITIALIZED);this._t(Xn.LOADED),this.bt(o)}),nt(S,"turbolinks:load",()=>{this._t(Xn.UNINITIALIZED),this.loadToolbar(o)})}return!0}wt(t){return this.loadToolbar(t)}maybeLoadEditor(t,n,r){return t===void 0&&(t=void 0),n===void 0&&(n=void 0),r===void 0&&(r=void 0),this.maybeLoadToolbar(t,n,r)}}var A5=it("[TracingHeaders]");class P5{constructor(t){this.xt=void 0,this.St=void 0,this.rt=()=>{var n,r;L(this.xt)&&((n=ne.__PosthogExtensions__)==null||(n=n.tracingHeadersPatchFns)==null||n._patchXHR(this._instance.config.__add_tracing_headers||[],this._instance.get_distinct_id(),this._instance.sessionManager)),L(this.St)&&((r=ne.__PosthogExtensions__)==null||(r=r.tracingHeadersPatchFns)==null||r._patchFetch(this._instance.config.__add_tracing_headers||[],this._instance.get_distinct_id(),this._instance.sessionManager))},this._instance=t}it(t){var n,r;(n=ne.__PosthogExtensions__)!=null&&n.tracingHeadersPatchFns&&t(),(r=ne.__PosthogExtensions__)==null||r.loadExternalDependency==null||r.loadExternalDependency(this._instance,"tracing-headers",o=>{if(o)return A5.error("failed to load script",o);t()})}startIfEnabledOrStop(){var t,n;this._instance.config.__add_tracing_headers?this.it(this.rt):((t=this.xt)==null||t.call(this),(n=this.St)==null||n.call(this),this.xt=void 0,this.St=void 0)}}var vs="Mobile",Wd="iOS",rn="Android",pl="Tablet",qk=rn+" "+pl,Vk="iPad",Gk="Apple",Yk=Gk+" Watch",xl="Safari",Ea="BlackBerry",Qk="Samsung",Kk=Qk+"Browser",Xk=Qk+" Internet",xo="Chrome",O5=xo+" OS",Jk=xo+" "+Wd,$f="Internet Explorer",Zk=$f+" "+vs,Lf="Opera",D5=Lf+" Mini",Ff="Edge",e2="Microsoft "+Ff,aa="Firefox",t2=aa+" "+Wd,fl="Nintendo",gl="PlayStation",ia="Xbox",s2=rn+" "+vs,n2=vs+" "+xl,wi="Windows",Lp=wi+" Phone",sb="Nokia",Fp="Ouya",r2="Generic",R5=r2+" "+vs.toLowerCase(),o2=r2+" "+pl.toLowerCase(),zp="Konqueror",Tt="(\\d+(\\.\\d+)?)",Gh=new RegExp("Version/"+Tt),M5=new RegExp(ia,"i"),$5=new RegExp(gl+" \\w+","i"),L5=new RegExp(fl+" \\w+","i"),zf=new RegExp(Ea+"|PlayBook|BB10","i"),F5={"NT3.51":"NT 3.11","NT4.0":"NT 4.0","5.0":"2000",5.1:"XP",5.2:"XP","6.0":"Vista",6.1:"7",6.2:"8",6.3:"8.1",6.4:"10","10.0":"10"},z5=(e,t)=>t&&me(t,Gk)||function(n){return me(n,xl)&&!me(n,xo)&&!me(n,rn)}(e),a2=function(e,t){return t=t||"",me(e," OPR/")&&me(e,"Mini")?D5:me(e," OPR/")?Lf:zf.test(e)?Ea:me(e,"IE"+vs)||me(e,"WPDesktop")?Zk:me(e,Kk)?Xk:me(e,Ff)||me(e,"Edg/")?e2:me(e,"FBIOS")?"Facebook "+vs:me(e,"UCWEB")||me(e,"UCBrowser")?"UC Browser":me(e,"CriOS")?Jk:me(e,"CrMo")||me(e,xo)?xo:me(e,rn)&&me(e,xl)?s2:me(e,"FxiOS")?t2:me(e.toLowerCase(),zp.toLowerCase())?zp:z5(e,t)?me(e,vs)?n2:xl:me(e,aa)?aa:me(e,"MSIE")||me(e,"Trident/")?$f:me(e,"Gecko")?aa:""},B5={[Zk]:[new RegExp("rv:"+Tt)],[e2]:[new RegExp(Ff+"?\\/"+Tt)],[xo]:[new RegExp("("+xo+"|CrMo)\\/"+Tt)],[Jk]:[new RegExp("CriOS\\/"+Tt)],"UC Browser":[new RegExp("(UCBrowser|UCWEB)\\/"+Tt)],[xl]:[Gh],[n2]:[Gh],[Lf]:[new RegExp("(Opera|OPR)\\/"+Tt)],[aa]:[new RegExp(aa+"\\/"+Tt)],[t2]:[new RegExp("FxiOS\\/"+Tt)],[zp]:[new RegExp("Konqueror[:/]?"+Tt,"i")],[Ea]:[new RegExp(Ea+" "+Tt),Gh],[s2]:[new RegExp("android\\s"+Tt,"i")],[Xk]:[new RegExp(Kk+"\\/"+Tt)],[$f]:[new RegExp("(rv:|MSIE )"+Tt)],Mozilla:[new RegExp("rv:"+Tt)]},W5=function(e,t){var n=a2(e,t),r=B5[n];if(L(r))return null;for(var o=0;o[ia,e&&e[1]||""]],[new RegExp(fl,"i"),[fl,""]],[new RegExp(gl,"i"),[gl,""]],[zf,[Ea,""]],[new RegExp(wi,"i"),(e,t)=>{if(/Phone/.test(t)||/WPDesktop/.test(t))return[Lp,""];if(new RegExp(vs).test(t)&&!/IEMobile\b/.test(t))return[wi+" "+vs,""];var n=/Windows NT ([0-9.]+)/i.exec(t);if(n&&n[1]){var r=n[1],o=F5[r]||"";return/arm/i.test(t)&&(o="RT"),[wi,o]}return[wi,""]}],[/((iPhone|iPad|iPod).*?OS (\d+)_(\d+)_?(\d+)?|iPhone)/,e=>{if(e&&e[3]){var t=[e[3],e[4],e[5]||"0"];return[Wd,t.join(".")]}return[Wd,""]}],[/(watch.*\/(\d+\.\d+\.\d+)|watch os,(\d+\.\d+),)/i,e=>{var t="";return e&&e.length>=3&&(t=L(e[2])?e[3]:e[2]),["watchOS",t]}],[new RegExp("("+rn+" (\\d+)\\.(\\d+)\\.?(\\d+)?|"+rn+")","i"),e=>{if(e&&e[2]){var t=[e[2],e[3],e[4]||"0"];return[rn,t.join(".")]}return[rn,""]}],[/Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/i,e=>{var t=["Mac OS X",""];if(e&&e[1]){var n=[e[1],e[2],e[3]||"0"];t[1]=n.join(".")}return t}],[/Mac/i,["Mac OS X",""]],[/CrOS/,[O5,""]],[/Linux|debian/i,["Linux",""]]],rb=function(e){return L5.test(e)?fl:$5.test(e)?gl:M5.test(e)?ia:new RegExp(Fp,"i").test(e)?Fp:new RegExp("("+Lp+"|WPDesktop)","i").test(e)?Lp:/iPad/.test(e)?Vk:/iPod/.test(e)?"iPod Touch":/iPhone/.test(e)?"iPhone":/(watch)(?: ?os[,/]|\d,\d\/)[\d.]+/i.test(e)?Yk:zf.test(e)?Ea:/(kobo)\s(ereader|touch)/i.test(e)?"Kobo":new RegExp(sb,"i").test(e)?sb:/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i.test(e)||/(kf[a-z]+)( bui|\)).+silk\//i.test(e)?"Kindle Fire":/(Android|ZTE)/i.test(e)?!new RegExp(vs).test(e)||/(9138B|TB782B|Nexus [97]|pixel c|HUAWEISHT|BTV|noble nook|smart ultra 6)/i.test(e)?/pixel[\daxl ]{1,6}/i.test(e)&&!/pixel c/i.test(e)||/(huaweimed-al00|tah-|APA|SM-G92|i980|zte|U304AA)/i.test(e)||/lmy47v/i.test(e)&&!/QTAQZ3/i.test(e)?rn:qk:rn:new RegExp("(pda|"+vs+")","i").test(e)?R5:new RegExp(pl,"i").test(e)&&!new RegExp(pl+" pc","i").test(e)?o2:""},gc="https?://(.*)",Wa=["gclid","gclsrc","dclid","gbraid","wbraid","fbclid","msclkid","twclid","li_fat_id","igshid","ttclid","rdt_cid","epik","qclid","sccid","irclid","_kx"],U5=Ba(["utm_source","utm_medium","utm_campaign","utm_content","utm_term","gad_source","mc_cid"],Wa),Ml="",H5=["li_fat_id"];function i2(e,t,n){if(!z)return{};var r,o=t?Ba([],Wa,n||[]):[],a=l2(Rl(z.URL,o,Ml),e),i=(r={},De(H5,function(l){var c=cn.q(l);r[l]=c||null}),r);return Je(i,a)}function l2(e,t){var n=U5.concat(t||[]),r={};return De(n,function(o){var a=Fd(e,o);r[o]=a||null}),r}function c2(e){var t=function(a){return a?a.search(gc+"google.([^/?]*)")===0?"google":a.search(gc+"bing.com")===0?"bing":a.search(gc+"yahoo.com")===0?"yahoo":a.search(gc+"duckduckgo.com")===0?"duckduckgo":null:null}(e),n=t!="yahoo"?"q":"p",r={};if(!zn(t)){r.$search_engine=t;var o=z?Fd(z.referrer,n):"";o.length&&(r.ph_keyword=o)}return r}function ob(){return navigator.language||navigator.userLanguage}function d2(){return(z==null?void 0:z.referrer)||"$direct"}function u2(e,t){var n=e?Ba([],Wa,t||[]):[],r=qt==null?void 0:qt.href.substring(0,1e3);return{r:d2().substring(0,1e3),u:r?Rl(r,n,Ml):void 0}}function h2(e){var t,{r:n,u:r}=e,o={$referrer:n,$referring_domain:n==null?void 0:n=="$direct"?"$direct":(t=Ld(n))==null?void 0:t.host};if(r){o.$current_url=r;var a=Ld(r);o.$host=a==null?void 0:a.host,o.$pathname=a==null?void 0:a.pathname;var i=l2(r);Je(o,i)}if(n){var l=c2(n);Je(o,l)}return o}function m2(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function q5(){try{return new Date().getTimezoneOffset()}catch{return}}function V5(e,t){if(!Bt)return{};var n,r,o,a=e?Ba([],Wa,t||[]):[],[i,l]=function(c){for(var d=0;d1e3?Bt.substring(0,997)+"...":Bt,$browser_version:W5(Bt,navigator.vendor),$browser_language:ob(),$browser_language_prefix:(n=ob(),typeof n=="string"?n.split("-")[0]:void 0),$screen_height:S==null?void 0:S.screen.height,$screen_width:S==null?void 0:S.screen.width,$viewport_height:S==null?void 0:S.innerHeight,$viewport_width:S==null?void 0:S.innerWidth,$lib:"web",$lib_version:vn.LIB_VERSION,$insert_id:Math.random().toString(36).substring(2,10)+Math.random().toString(36).substring(2,10),$time:Date.now()/1e3})}var Gn=it("[Web Vitals]"),ab=9e5;class G5{constructor(t){var n;this.Et=!1,this.P=!1,this.N={url:void 0,metrics:[],firstMetricTimestamp:void 0},this.$t=()=>{clearTimeout(this.kt),this.N.metrics.length!==0&&(this._instance.capture("$web_vitals",this.N.metrics.reduce((r,o)=>G({},r,{["$web_vitals_"+o.name+"_event"]:G({},o),["$web_vitals_"+o.name+"_value"]:o.value}),{})),this.N={url:void 0,metrics:[],firstMetricTimestamp:void 0})},this.Pt=r=>{var o,a=(o=this._instance.sessionManager)==null?void 0:o.checkAndGetSessionAndWindowId(!0);if(L(a))Gn.error("Could not read session ID. Dropping metrics!");else{this.N=this.N||{url:void 0,metrics:[],firstMetricTimestamp:void 0};var i=this.Tt();L(i)||(Ie(r==null?void 0:r.name)||Ie(r==null?void 0:r.value)?Gn.error("Invalid metric received",r):this.Rt&&r.value>=this.Rt?Gn.error("Ignoring metric with value >= "+this.Rt,r):(this.N.url!==i&&(this.$t(),this.kt=setTimeout(this.$t,this.flushToCaptureTimeoutMs)),L(this.N.url)&&(this.N.url=i),this.N.firstMetricTimestamp=L(this.N.firstMetricTimestamp)?Date.now():this.N.firstMetricTimestamp,r.attribution&&r.attribution.interactionTargetElement&&(r.attribution.interactionTargetElement=void 0),this.N.metrics.push(G({},r,{$current_url:i,$session_id:a.sessionId,$window_id:a.windowId,timestamp:Date.now()})),this.N.metrics.length===this.allowedMetrics.length&&this.$t()))}},this.rt=()=>{var r,o,a,i,l=ne.__PosthogExtensions__;L(l)||L(l.postHogWebVitalsCallbacks)||({onLCP:r,onCLS:o,onFCP:a,onINP:i}=l.postHogWebVitalsCallbacks),r&&o&&a&&i?(this.allowedMetrics.indexOf("LCP")>-1&&r(this.Pt.bind(this)),this.allowedMetrics.indexOf("CLS")>-1&&o(this.Pt.bind(this)),this.allowedMetrics.indexOf("FCP")>-1&&a(this.Pt.bind(this)),this.allowedMetrics.indexOf("INP")>-1&&i(this.Pt.bind(this)),this.P=!0):Gn.error("web vitals callbacks not loaded - not starting")},this._instance=t,this.Et=!((n=this._instance.persistence)==null||!n.props[Uy]),this.startIfEnabled()}get allowedMetrics(){var t,n,r=ct(this._instance.config.capture_performance)?(t=this._instance.config.capture_performance)==null?void 0:t.web_vitals_allowed_metrics:void 0;return L(r)?((n=this._instance.persistence)==null?void 0:n.props[Hy])||["CLS","FCP","INP","LCP"]:r}get flushToCaptureTimeoutMs(){return(ct(this._instance.config.capture_performance)?this._instance.config.capture_performance.web_vitals_delayed_flush_ms:void 0)||5e3}get Rt(){var t=ct(this._instance.config.capture_performance)&&un(this._instance.config.capture_performance.__web_vitals_max_value)?this._instance.config.capture_performance.__web_vitals_max_value:ab;return 0{o?Gn.error("failed to load script",o):t()})}Tt(){var t=S?S.location.href:void 0;if(t){var n=this._instance.config.mask_personal_data_properties,r=this._instance.config.custom_personal_data_properties,o=n?Ba([],Wa,r||[]):[];return Rl(t,o,Ml)}Gn.error("Could not determine current URL")}}var Y5=it("[Heatmaps]");function ib(e){return ct(e)&&"clientX"in e&&"clientY"in e&&un(e.clientX)&&un(e.clientY)}class Q5{constructor(t){var n;this.Et=!1,this.P=!1,this.It=null,this.instance=t,this.Et=!((n=this.instance.persistence)==null||!n.props[Tp]),this.rageclicks=new zk(t.config.rageclick)}get flushIntervalMilliseconds(){var t=5e3;return ct(this.instance.config.capture_heatmaps)&&this.instance.config.capture_heatmaps.flush_interval_milliseconds&&(t=this.instance.config.capture_heatmaps.flush_interval_milliseconds),t}get isEnabled(){return L(this.instance.config.capture_heatmaps)?L(this.instance.config.enable_heatmaps)?this.Et:this.instance.config.enable_heatmaps:this.instance.config.capture_heatmaps!==!1}startIfEnabled(){if(this.isEnabled){if(this.P)return;Y5.info("starting..."),this.Ft(),this.Ct()}else{var t;clearInterval((t=this.It)!==null&&t!==void 0?t:void 0),this.Mt(),this.getAndClearBuffer()}}onRemoteConfig(t){var n=!!t.heatmaps;this.instance.persistence&&this.instance.persistence.register({[Tp]:n}),this.Et=n,this.startIfEnabled()}getAndClearBuffer(){var t=this.N;return this.N=void 0,t}Ot(t){this.At(t.originalEvent,"deadclick")}Ct(){this.It&&clearInterval(this.It),this.It=function(t){return(t==null?void 0:t.visibilityState)==="visible"}(z)?setInterval(this.Dt.bind(this),this.flushIntervalMilliseconds):null}Ft(){S&&z&&(this.jt=this.Dt.bind(this),nt(S,"beforeunload",this.jt),this.Lt=t=>this.At(t||(S==null?void 0:S.event)),nt(z,"click",this.Lt,{capture:!0}),this.Nt=t=>this.Ut(t||(S==null?void 0:S.event)),nt(z,"mousemove",this.Nt,{capture:!0}),this.zt=new Wk(this.instance,N5,this.Ot.bind(this)),this.zt.startIfEnabled(),this.Ht=this.Ct.bind(this),nt(z,"visibilitychange",this.Ht),this.P=!0)}Mt(){var t;S&&z&&(this.jt&&S.removeEventListener("beforeunload",this.jt),this.Lt&&z.removeEventListener("click",this.Lt,{capture:!0}),this.Nt&&z.removeEventListener("mousemove",this.Nt,{capture:!0}),this.Ht&&z.removeEventListener("visibilitychange",this.Ht),clearTimeout(this.Bt),(t=this.zt)==null||t.stop(),this.P=!1)}qt(t,n){var r=this.instance.scrollManager.scrollY(),o=this.instance.scrollManager.scrollX(),a=this.instance.scrollManager.scrollElement(),i=function(l,c,d){for(var u=l;u&&Fu(u)&&!Cr(u,"body");){if(u===d)return!1;if(me(c,S==null?void 0:S.getComputedStyle(u).position))return!0;u=Ok(u)}return!1}(Ak(t),["fixed","sticky"],a);return{x:t.clientX+(i?0:o),y:t.clientY+(i?0:r),target_fixed:i,type:n}}At(t,n){var r;if(n===void 0&&(n="click"),!qy(t.target)&&ib(t)){var o=this.qt(t,n);(r=this.rageclicks)!=null&&r.isRageClick(t.clientX,t.clientY,new Date().getTime())&&this.Wt(G({},o,{type:"rageclick"})),this.Wt(o)}}Ut(t){!qy(t.target)&&ib(t)&&(clearTimeout(this.Bt),this.Bt=setTimeout(()=>{this.Wt(this.qt(t,"mousemove"))},500))}Wt(t){if(S){var n=S.location.href,r=this.instance.config.mask_personal_data_properties,o=this.instance.config.custom_personal_data_properties,a=r?Ba([],Wa,o||[]):[],i=Rl(n,a,Ml);this.N=this.N||{},this.N[i]||(this.N[i]=[]),this.N[i].push(t)}}Dt(){this.N&&!Go(this.N)&&this.instance.capture("$$heatmap",{$heatmap_data:this.getAndClearBuffer()})}}class K5{constructor(t){this._instance=t}doPageView(t,n){var r,o=this.Gt(t,n);return this.Vt={pathname:(r=S==null?void 0:S.location.pathname)!==null&&r!==void 0?r:"",pageViewId:n,timestamp:t},this._instance.scrollManager.resetContext(),o}doPageLeave(t){var n;return this.Gt(t,(n=this.Vt)==null?void 0:n.pageViewId)}doEvent(){var t;return{$pageview_id:(t=this.Vt)==null?void 0:t.pageViewId}}Gt(t,n){var r=this.Vt;if(!r)return{$pageview_id:n};var o={$pageview_id:n,$prev_pageview_id:r.pageViewId},a=this._instance.scrollManager.getContext();if(a&&!this._instance.config.disable_scroll_properties){var{maxScrollHeight:i,lastScrollY:l,maxScrollY:c,maxContentHeight:d,lastContentY:u,maxContentY:h}=a;if(!(L(i)||L(l)||L(c)||L(d)||L(u)||L(h))){i=Math.ceil(i),l=Math.ceil(l),c=Math.ceil(c),d=Math.ceil(d),u=Math.ceil(u),h=Math.ceil(h);var p=i<=1?1:nn(l/i,0,1,U),m=i<=1?1:nn(c/i,0,1,U),w=d<=1?1:nn(u/d,0,1,U),x=d<=1?1:nn(h/d,0,1,U);o=Je(o,{$prev_pageview_last_scroll:l,$prev_pageview_last_scroll_percentage:p,$prev_pageview_max_scroll:c,$prev_pageview_max_scroll_percentage:m,$prev_pageview_last_content:u,$prev_pageview_last_content_percentage:w,$prev_pageview_max_content:h,$prev_pageview_max_content_percentage:x})}}return r.pathname&&(o.$prev_pageview_pathname=r.pathname),r.timestamp&&(o.$prev_pageview_duration=(t.getTime()-r.timestamp.getTime())/1e3),o}}var ys=Uint8Array,Gt=Uint16Array,Ta=Uint32Array,Bf=new ys([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Wf=new ys([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),lb=new ys([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),p2=function(e,t){for(var n=new Gt(31),r=0;r<31;++r)n[r]=t+=1<>>1|(21845&ze)<<1;zr=(61680&(zr=(52428&zr)>>>2|(13107&zr)<<2))>>>4|(3855&zr)<<4,f2[ze]=((65280&zr)>>>8|(255&zr)<<8)>>>1}var Fi=function(e,t,n){for(var r=e.length,o=0,a=new Gt(t);o>>15-e[o];return i},fo=new ys(288);for(ze=0;ze<144;++ze)fo[ze]=8;for(ze=144;ze<256;++ze)fo[ze]=9;for(ze=256;ze<280;++ze)fo[ze]=7;for(ze=280;ze<288;++ze)fo[ze]=8;var Ud=new ys(32);for(ze=0;ze<32;++ze)Ud[ze]=5;var J5=Fi(fo,9),Z5=Fi(Ud,5),g2=function(e){return(e/8>>0)+(7&e&&1)},v2=function(e,t,n){(n==null||n>e.length)&&(n=e.length);var r=new(e instanceof Gt?Gt:e instanceof Ta?Ta:ys)(n-t);return r.set(e.subarray(t,n)),r},pn=function(e,t,n){n<<=7&t;var r=t/8>>0;e[r]|=n,e[r+1]|=n>>>8},ii=function(e,t,n){n<<=7&t;var r=t/8>>0;e[r]|=n,e[r+1]|=n>>>8,e[r+2]|=n>>>16},Yh=function(e,t){for(var n=[],r=0;rp&&(p=a[r].s);var m=new Gt(p+1),w=Wp(n[u-1],m,0);if(w>t){r=0;var x=0,b=w-t,v=1<t))break;x+=v-(1<>>=b;x>0;){var y=a[r].s;m[y]=0&&x;--r){var j=a[r].s;m[j]==t&&(--m[j],++x)}w=t}return[new ys(m),w]},Wp=function(e,t,n){return e.s==-1?Math.max(Wp(e.l,t,n+1),Wp(e.r,t,n+1)):t[e.s]=n},db=function(e){for(var t=e.length;t&&!e[--t];);for(var n=new Gt(++t),r=0,o=e[0],a=1,i=function(c){n[r++]=c},l=1;l<=t;++l)if(e[l]==o&&l!=t)++a;else{if(!o&&a>2){for(;a>138;a-=138)i(32754);a>2&&(i(a>10?a-11<<5|28690:a-3<<5|12305),a=0)}else if(a>3){for(i(o),--a;a>6;a-=6)i(8304);a>2&&(i(a-3<<5|8208),a=0)}for(;a--;)i(o);a=1,o=e[l]}return[n.subarray(0,r),t]},li=function(e,t){for(var n=0,r=0;r>>8,e[o+2]=255^e[o],e[o+3]=255^e[o+1];for(var a=0;a4&&!A[lb[M-1]];--M);var K,O,X,q,Y=d+5<<3,E=li(o,fo)+li(a,Ud)+i,T=li(o,p)+li(a,x)+i+14+3*M+li(k,A)+(2*k[16]+3*k[17]+7*k[18]);if(Y<=E&&Y<=T)return Up(t,u,e.subarray(c,c+d));if(pn(t,u,1+(T15&&(pn(t,u,ee[C]>>>5&127),u+=ee[C]>>>12)}}}else K=J5,O=fo,X=Z5,q=Ud;for(C=0;C255){J=r[C]>>>18&31,ii(t,u,K[J+257]),u+=O[J+257],J>7&&(pn(t,u,r[C]>>>23&31),u+=Bf[J]);var we=31&r[C];ii(t,u,X[we]),u+=q[we],we>3&&(ii(t,u,r[C]>>>5&8191),u+=Wf[we])}else ii(t,u,K[r[C]]),u+=O[r[C]];return ii(t,u,K[256]),u+O[256]},eO=new Ta([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),tO=function(){for(var e=new Ta(256),t=0;t<256;++t){for(var n=t,r=9;--r;)n=(1&n&&3988292384)^n>>>1;e[t]=n}return e}(),sO=function(e,t,n,r,o){return function(a,i,l,c,d,u){var h=a.length,p=new ys(c+h+5*(1+Math.floor(h/7e3))+d),m=p.subarray(c,p.length-d),w=0;if(!i||h<8)for(var x=0;x<=h;x+=65535){var b=x+65535;b>>13,y=8191&v,j=(1<7e3||X>24576)&&Z>423){w=ub(a,m,0,A,H,M,O,X,Y,x-Y,w),X=K=O=0,Y=x;for(var W=0;W<286;++W)H[W]=0;for(W=0;W<30;++W)M[W]=0}var ee=2,J=0,we=y,Ne=T-$&32767;if(Z>2&&E==P(x-Ne))for(var F=Math.min(g,Z)-1,ke=Math.min(32767,x),Ue=Math.min(258,Z);Ne<=ke&&--we&&T!=$;){if(a[x+ee]==a[x+ee-Ne]){for(var ce=0;ceee){if(ee=ce,J=Ne,ce>F)break;var xe=Math.min(Ne,ce-2),fe=0;for(W=0;Wfe&&(fe=Et,$=tt)}}}Ne+=(T=$)-($=N[T])+32768&32767}if(J){A[X++]=268435456|Bp[ee]<<18|cb[J];var qs=31&Bp[ee],js=31&cb[J];O+=Bf[qs]+Wf[js],++H[257+qs],++M[js],q=x+ee,++K}else A[X++]=a[x],++H[a[x]]}}w=ub(a,m,u,A,H,M,O,X,Y,x-Y,w)}return v2(p,0,c+g2(w)+d)}(e,t.level==null?6:t.level,t.mem==null?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(e.length)))):12+t.mem,n,r,!0)},Qh=function(e,t,n){for(;n;++t)e[t]=n,n>>>=8};function nO(e,t){t===void 0&&(t={});var n=function(){var l=4294967295;return{p:function(c){for(var d=l,u=0;u>>8;l=d},d:function(){return 4294967295^l}}}(),r=e.length;n.p(e);var o,a=sO(e,t,10+((o=t).filename&&o.filename.length+1||0),8),i=a.length;return function(l,c){var d=c.filename;if(l[0]=31,l[1]=139,l[2]=8,l[8]=c.level<2?4:c.level==9?2:0,l[9]=3,c.mtime!=0&&Qh(l,4,Math.floor(new Date(c.mtime||Date.now())/1e3)),d){l[3]=8;for(var u=0;u<=d.length;++u)l[u+10]=d.charCodeAt(u)}}(a,t),Qh(a,i-8,n.d()),Qh(a,i-4,r),a}var rO=function(e){var t,n,r,o,a="";for(t=n=0,r=(e=(e+"").replace(/\r\n/g,` `).replace(/\r/g,` `)).length,o=0;o127&&i<2048?String.fromCharCode(i>>6|192,63&i|128):String.fromCharCode(i>>12|224,i>>6&63|128,63&i|128),zn(l)||(n>t&&(a+=e.substring(t,n)),a+=l,t=n=o+1)}return n>t&&(a+=e.substring(t,e.length)),a},oO=!!kp||!!Np,hb="text/plain",Hd=function(e,t,n){var r;n===void 0&&(n=!0);var[o,a]=e.split("?"),i=G({},t),l=(r=a==null?void 0:a.split("&").map(d=>{var u,[h,p]=d.split("="),m=n&&(u=i[h])!==null&&u!==void 0?u:p;return delete i[h],h+"="+m}))!==null&&r!==void 0?r:[],c=d5(i);return c&&l.push(c),o+"?"+l.join("&")},ji=(e,t)=>JSON.stringify(e,(n,r)=>typeof r=="bigint"?r.toString():r,t),Kh=e=>{var{data:t,compression:n}=e;if(t){if(n===Nn.GZipJS){var r=nO(function(c,d){var u=c.length;if(typeof TextEncoder<"u")return new TextEncoder().encode(c);for(var h=new ys(c.length+(c.length>>>1)),p=0,m=function(v){h[p++]=v},w=0;wh.length){var x=new ys(p+8+(u-w<<1));x.set(h),h=x}var b=c.charCodeAt(w);b<128||d?m(b):b<2048?(m(192|b>>>6),m(128|63&b)):b>55295&&b<57344?(m(240|(b=65536+(1047552&b)|1023&c.charCodeAt(++w))>>>18),m(128|b>>>12&63),m(128|b>>>6&63),m(128|63&b)):(m(224|b>>>12),m(128|b>>>6&63),m(128|63&b))}return v2(h,0,p)}(ji(t)),{mtime:0}),o=new Blob([r],{type:hb});return{contentType:hb,body:o,estimatedSize:o.size}}if(n===Nn.Base64){var a=function(c){var d,u,h,p,m,w="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",x=0,b=0,v="",g=[];if(!c)return c;c=rO(c);do d=(m=c.charCodeAt(x++)<<16|c.charCodeAt(x++)<<8|c.charCodeAt(x++))>>18&63,u=m>>12&63,h=m>>6&63,p=63&m,g[b++]=w.charAt(d)+w.charAt(u)+w.charAt(h)+w.charAt(p);while(x"data="+encodeURIComponent(typeof c=="string"?c:ji(c)))(a);return{contentType:"application/x-www-form-urlencoded",body:i,estimatedSize:new Blob([i]).size}}var l=ji(t);return{contentType:"application/json",body:l,estimatedSize:new Blob([l]).size}}},Bc=[];Np&&Bc.push({transport:"fetch",method:e=>{var t,n,{contentType:r,body:o,estimatedSize:a}=(t=Kh(e))!==null&&t!==void 0?t:{},i=new Headers;De(e.headers,function(u,h){i.append(h,u)}),r&&i.append("Content-Type",r);var l=e.url,c=null;if(Ay){var d=new Ay;c={signal:d.signal,timeout:setTimeout(()=>d.abort(),e.timeout)}}Np(l,G({method:(e==null?void 0:e.method)||"GET",headers:i,keepalive:e.method==="POST"&&(a||0)<52428.8,body:o,signal:(n=c)==null?void 0:n.signal},e.fetchOptions)).then(u=>u.text().then(h=>{var p={statusCode:u.status,text:h};if(u.status===200)try{p.json=JSON.parse(h)}catch(m){U.error(m)}e.callback==null||e.callback(p)})).catch(u=>{U.error(u),e.callback==null||e.callback({statusCode:0,text:u})}).finally(()=>c?clearTimeout(c.timeout):null)}}),kp&&Bc.push({transport:"XHR",method:e=>{var t,n=new kp;n.open(e.method||"GET",e.url,!0);var{contentType:r,body:o}=(t=Kh(e))!==null&&t!==void 0?t:{};De(e.headers,function(a,i){n.setRequestHeader(i,a)}),r&&n.setRequestHeader("Content-Type",r),e.timeout&&(n.timeout=e.timeout),e.disableXHRCredentials||(n.withCredentials=!0),n.onreadystatechange=()=>{if(n.readyState===4){var a={statusCode:n.status,text:n.responseText};if(n.status===200)try{a.json=JSON.parse(n.responseText)}catch{}e.callback==null||e.callback(a)}},n.send(o)}}),as!=null&&as.sendBeacon&&Bc.push({transport:"sendBeacon",method:e=>{var t=Hd(e.url,{beacon:"1"});try{var n,{contentType:r,body:o}=(n=Kh(e))!==null&&n!==void 0?n:{},a=typeof o=="string"?new Blob([o],{type:r}):o;as.sendBeacon(t,a)}catch{}}});var qd=function(e,t){if(!function(n){try{new RegExp(n)}catch{return!1}return!0}(t))return!1;try{return new RegExp(t).test(e)}catch{return!1}};function mb(e,t,n){return ji({distinct_id:e,userPropertiesToSet:t,userPropertiesToSetOnce:n})}var y2={exact:(e,t)=>t.some(n=>e.some(r=>n===r)),is_not:(e,t)=>t.every(n=>e.every(r=>n!==r)),regex:(e,t)=>t.some(n=>e.some(r=>qd(n,r))),not_regex:(e,t)=>t.every(n=>e.every(r=>!qd(n,r))),icontains:(e,t)=>t.map(vc).some(n=>e.map(vc).some(r=>n.includes(r))),not_icontains:(e,t)=>t.map(vc).every(n=>e.map(vc).every(r=>!n.includes(r)))},vc=e=>e.toLowerCase();function b2(e,t){return!e||Object.entries(e).every(n=>{var[r,o]=n,a=t==null?void 0:t[r];if(L(a)||zn(a))return!1;var i=[String(a)],l=y2[o.operator];return!!l&&l(o.values,i)})}var Xh=it("[Error tracking]");class aO{constructor(t){var n,r;this.Jt=[],this.Kt=new I8([new F8,new Y8,new B8,new z8,new V8,new q8,new U8,new G8],L8("web:javascript",D8,$8)),this._instance=t,this.Jt=(n=(r=this._instance.persistence)==null?void 0:r.get_property(Ip))!==null&&n!==void 0?n:[]}onRemoteConfig(t){var n,r,o,a=(n=(r=t.errorTracking)==null?void 0:r.suppressionRules)!==null&&n!==void 0?n:[],i=(o=t.errorTracking)==null?void 0:o.captureExtensionExceptions;this.Jt=a,this._instance.persistence&&this._instance.persistence.register({[Ip]:this.Jt,[Wy]:i})}get Yt(){var t,n=!!this._instance.get_property(Wy),r=this._instance.config.error_tracking.captureExtensionExceptions;return(t=r??n)!==null&&t!==void 0&&t}buildProperties(t,n){return this.Kt.buildFromUnknown(t,{syntheticException:n==null?void 0:n.syntheticException,mechanism:{handled:n==null?void 0:n.handled}})}sendExceptionEvent(t){var n=t.$exception_list;if(this.Xt(n)){if(this.Qt(n))return void Xh.info("Skipping exception capture because a suppression rule matched");if(!this.Yt&&this.Zt(n))return void Xh.info("Skipping exception capture because it was thrown by an extension");if(!this._instance.config.error_tracking.__capturePostHogExceptions&&this.ti(n))return void Xh.info("Skipping exception capture because it was thrown by the PostHog SDK")}return this._instance.capture("$exception",t,{_noTruncate:!0,_batchKey:"exceptionEvent"})}Qt(t){if(t.length===0)return!1;var n=t.reduce((r,o)=>{var{type:a,value:i}=o;return ot(a)&&a.length>0&&r.$exception_types.push(a),ot(i)&&i.length>0&&r.$exception_values.push(i),r},{$exception_types:[],$exception_values:[]});return this.Jt.some(r=>{var o=r.values.map(a=>{var i,l=y2[a.operator],c=Ae(a.value)?a.value:[a.value],d=(i=n[a.key])!==null&&i!==void 0?i:[];return c.length>0&&l(c,d)});return r.type==="OR"?o.some(Boolean):o.every(Boolean)})}Zt(t){return t.flatMap(n=>{var r,o;return(r=(o=n.stacktrace)==null?void 0:o.frames)!==null&&r!==void 0?r:[]}).some(n=>n.filename&&n.filename.startsWith("chrome-extension://"))}ti(t){if(t.length>0){var n,r,o,a,i=(n=(r=t[0].stacktrace)==null?void 0:r.frames)!==null&&n!==void 0?n:[],l=i[i.length-1];return(o=l==null||(a=l.filename)==null?void 0:a.includes("posthog.com/static"))!==null&&o!==void 0&&o}return!1}Xt(t){return!Ie(t)&&Ae(t)}}var Ts=it("[FeatureFlags]"),ci=it("[FeatureFlags]",{debugEnabled:!0}),Jh="$active_feature_flags",Ao="$override_feature_flags",pb="$feature_flag_payloads",di="$override_feature_flag_payloads",xb="$feature_flag_request_id",fb="$feature_flag_evaluated_at",gb=e=>{var t={};for(var[n,r]of zc(e||{}))r&&(t[n]=r);return t},iO=e=>{var t=e.flags;return t?(e.featureFlags=Object.fromEntries(Object.keys(t).map(n=>{var r;return[n,(r=t[n].variant)!==null&&r!==void 0?r:t[n].enabled]})),e.featureFlagPayloads=Object.fromEntries(Object.keys(t).filter(n=>t[n].enabled).filter(n=>{var r;return(r=t[n].metadata)==null?void 0:r.payload}).map(n=>{var r;return[n,(r=t[n].metadata)==null?void 0:r.payload]}))):Ts.warn("Using an older version of the feature flags endpoint. Please upgrade your PostHog server to the latest version"),e},lO=function(e){return e.FeatureFlags="feature_flags",e.Recordings="recordings",e}({});class cO{constructor(t){this.ii=!1,this.ei=!1,this.ri=!1,this.si=!1,this.ni=!1,this.oi=!1,this.ai=!1,this._instance=t,this.featureFlagEventHandlers=[]}li(){var t=this._instance.config.evaluation_environments;return t!=null&&t.length?t.filter(n=>{var r=n&&typeof n=="string"&&n.trim().length>0;return r||Ts.error("Invalid evaluation environment found:",n,"Expected non-empty string"),r}):[]}ui(){return this.li().length>0}flags(){if(this._instance.config.__preview_remote_config)this.oi=!0;else{var t=!this.hi&&(this._instance.config.advanced_disable_feature_flags||this._instance.config.advanced_disable_feature_flags_on_first_load);this.vi({disableFlags:t})}}get hasLoadedFlags(){return this.ei}getFlags(){return Object.keys(this.getFlagVariants())}getFlagsWithDetails(){var t=this._instance.get_property(Ap),n=this._instance.get_property(Ao),r=this._instance.get_property(di);if(!r&&!n)return t||{};var o=Je({},t||{}),a=[...new Set([...Object.keys(r||{}),...Object.keys(n||{})])];for(var i of a){var l,c,d=o[i],u=n==null?void 0:n[i],h=L(u)?(l=d==null?void 0:d.enabled)!==null&&l!==void 0&&l:!!u,p=L(u)?d.variant:typeof u=="string"?u:void 0,m=r==null?void 0:r[i],w=G({},d,{enabled:h,variant:h?p??(d==null?void 0:d.variant):void 0});h!==(d==null?void 0:d.enabled)&&(w.original_enabled=d==null?void 0:d.enabled),p!==(d==null?void 0:d.variant)&&(w.original_variant=d==null?void 0:d.variant),m&&(w.metadata=G({},d==null?void 0:d.metadata,{payload:m,original_payload:d==null||(c=d.metadata)==null?void 0:c.payload})),o[i]=w}return this.ii||(Ts.warn(" Overriding feature flag details!",{flagDetails:t,overriddenPayloads:r,finalDetails:o}),this.ii=!0),o}getFlagVariants(){var t=this._instance.get_property(Yo),n=this._instance.get_property(Ao);if(!n)return t||{};for(var r=Je({},t),o=Object.keys(n),a=0;a{this.vi()},5))}di(){clearTimeout(this.hi),this.hi=void 0}ensureFlagsLoaded(){this.ei||this.ri||this.hi||this.reloadFeatureFlags()}setAnonymousDistinctId(t){this.$anon_distinct_id=t}setReloadingPaused(t){this.si=t}vi(t){var n;if(this.di(),!this._instance.O())if(this.ri)this.ni=!0;else{var r={token:this._instance.config.token,distinct_id:this._instance.get_distinct_id(),groups:this._instance.getGroups(),$anon_distinct_id:this.$anon_distinct_id,person_properties:G({},((n=this._instance.persistence)==null?void 0:n.get_initial_props())||{},this._instance.get_property(bi)||{}),group_properties:this._instance.get_property(Hr)};(t!=null&&t.disableFlags||this._instance.config.advanced_disable_feature_flags)&&(r.disable_flags=!0),this.ui()&&(r.evaluation_environments=this.li());var o=this._instance.config.__preview_remote_config,a=o?"/flags/?v=2":"/flags/?v=2&config=true",i=this._instance.config.advanced_only_evaluate_survey_feature_flags?"&only_evaluate_survey_feature_flags=true":"",l=this._instance.requestRouter.endpointFor("flags",a+i);o&&(r.timezone=m2()),this.ri=!0,this._instance.ci({method:"POST",url:l,data:r,compression:this._instance.config.disable_compression?void 0:Nn.Base64,timeout:this._instance.config.feature_flag_request_timeout_ms,callback:c=>{var d,u,h=!0;if(c.statusCode===200&&(this.ni||(this.$anon_distinct_id=void 0),h=!1),this.ri=!1,this.oi||(this.oi=!0,this._instance.fi((u=c.json)!==null&&u!==void 0?u:{})),!r.disable_flags||this.ni)if(this.ai=!h,c.json&&(d=c.json.quotaLimited)!=null&&d.includes(lO.FeatureFlags))Ts.warn("You have hit your feature flags quota limit, and will not be able to load feature flags until the quota is reset. Please visit https://posthog.com/docs/billing/limits-alerts to learn more.");else{var p;r.disable_flags||this.receivedFeatureFlags((p=c.json)!==null&&p!==void 0?p:{},h),this.ni&&(this.ni=!1,this.vi())}}})}}getFeatureFlag(t,n){if(n===void 0&&(n={}),this.ei||this.getFlags()&&this.getFlags().length>0){var r=this.getFlagVariants()[t],o=""+r,a=this._instance.get_property(xb)||void 0,i=this._instance.get_property(fb)||void 0,l=this._instance.get_property(Dd)||{};if((n.send_event||!("send_event"in n))&&(!(t in l)||!l[t].includes(o))){var c,d,u,h,p,m,w,x,b;Ae(l[t])?l[t].push(o):l[t]=[o],(c=this._instance.persistence)==null||c.register({[Dd]:l});var v=this.getFeatureFlagDetails(t),g={$feature_flag:t,$feature_flag_response:r,$feature_flag_payload:this.getFeatureFlagPayload(t)||null,$feature_flag_request_id:a,$feature_flag_evaluated_at:i,$feature_flag_bootstrapped_response:((d=this._instance.config.bootstrap)==null||(d=d.featureFlags)==null?void 0:d[t])||null,$feature_flag_bootstrapped_payload:((u=this._instance.config.bootstrap)==null||(u=u.featureFlagPayloads)==null?void 0:u[t])||null,$used_bootstrap_value:!this.ai};L(v==null||(h=v.metadata)==null?void 0:h.version)||(g.$feature_flag_version=v.metadata.version);var y,j=(p=v==null||(m=v.reason)==null?void 0:m.description)!==null&&p!==void 0?p:v==null||(w=v.reason)==null?void 0:w.code;j&&(g.$feature_flag_reason=j),v!=null&&(x=v.metadata)!=null&&x.id&&(g.$feature_flag_id=v.metadata.id),L(v==null?void 0:v.original_variant)&&L(v==null?void 0:v.original_enabled)||(g.$feature_flag_original_response=L(v.original_variant)?v.original_enabled:v.original_variant),v!=null&&(b=v.metadata)!=null&&b.original_payload&&(g.$feature_flag_original_payload=v==null||(y=v.metadata)==null?void 0:y.original_payload),this._instance.capture("$feature_flag_called",g)}return r}Ts.warn('getFeatureFlag for key "'+t+`" failed. Feature flags didn't load in time.`)}getFeatureFlagDetails(t){return this.getFlagsWithDetails()[t]}getFeatureFlagPayload(t){return this.getFlagPayloads()[t]}getRemoteConfigPayload(t,n){var r=this._instance.config.token,o={distinct_id:this._instance.get_distinct_id(),token:r};this.ui()&&(o.evaluation_environments=this.li()),this._instance.ci({method:"POST",url:this._instance.requestRouter.endpointFor("flags","/flags/?v=2&config=true"),data:o,compression:this._instance.config.disable_compression?void 0:Nn.Base64,timeout:this._instance.config.feature_flag_request_timeout_ms,callback:a=>{var i,l=(i=a.json)==null?void 0:i.featureFlagPayloads;n((l==null?void 0:l[t])||void 0)}})}isFeatureEnabled(t,n){if(n===void 0&&(n={}),this.ei||this.getFlags()&&this.getFlags().length>0){var r=this.getFeatureFlag(t,n);return L(r)?void 0:!!r}Ts.warn('isFeatureEnabled for key "'+t+`" failed. Feature flags didn't load in time.`)}addFeatureFlagsHandler(t){this.featureFlagEventHandlers.push(t)}removeFeatureFlagsHandler(t){this.featureFlagEventHandlers=this.featureFlagEventHandlers.filter(n=>n!==t)}receivedFeatureFlags(t,n){if(this._instance.persistence){this.ei=!0;var r=this.getFlagVariants(),o=this.getFlagPayloads(),a=this.getFlagsWithDetails();(function(i,l,c,d,u){c===void 0&&(c={}),d===void 0&&(d={}),u===void 0&&(u={});var h=iO(i),p=h.flags,m=h.featureFlags,w=h.featureFlagPayloads;if(m){var x=i.requestId,b=i.evaluatedAt;if(Ae(m)){Ts.warn("v1 of the feature flags endpoint is deprecated. Please use the latest version.");var v={};if(m)for(var g=0;gthis.removeFeatureFlagsHandler(t)}updateEarlyAccessFeatureEnrollment(t,n,r){var o,a=(this._instance.get_property(yi)||[]).find(d=>d.flagKey===t),i={["$feature_enrollment/"+t]:n},l={$feature_flag:t,$feature_enrollment:n,$set:i};a&&(l.$early_access_feature_name=a.name),r&&(l.$feature_enrollment_stage=r),this._instance.capture("$feature_enrollment_update",l),this.setPersonPropertiesForFlags(i,!1);var c=G({},this.getFlagVariants(),{[t]:n});(o=this._instance.persistence)==null||o.register({[Jh]:Object.keys(gb(c)),[Yo]:c}),this.pi()}getEarlyAccessFeatures(t,n,r){n===void 0&&(n=!1);var o=this._instance.get_property(yi),a=r?"&"+r.map(i=>"stage="+i).join("&"):"";if(o&&!n)return t(o);this._instance.ci({url:this._instance.requestRouter.endpointFor("api","/api/early_access_features/?token="+this._instance.config.token+a),method:"GET",callback:i=>{var l,c;if(i.json){var d=i.json.earlyAccessFeatures;return(l=this._instance.persistence)==null||l.unregister(yi),(c=this._instance.persistence)==null||c.register({[yi]:d}),t(d)}}})}gi(){var t=this.getFlags(),n=this.getFlagVariants();return{flags:t.filter(r=>n[r]),flagVariants:Object.keys(n).filter(r=>n[r]).reduce((r,o)=>(r[o]=n[o],r),{})}}pi(t){var{flags:n,flagVariants:r}=this.gi();this.featureFlagEventHandlers.forEach(o=>o(n,r,{errorsLoading:t}))}setPersonPropertiesForFlags(t,n){n===void 0&&(n=!0);var r=this._instance.get_property(bi)||{};this._instance.register({[bi]:G({},r,t)}),n&&this._instance.reloadFeatureFlags()}resetPersonPropertiesForFlags(){this._instance.unregister(bi)}setGroupPropertiesForFlags(t,n){n===void 0&&(n=!0);var r=this._instance.get_property(Hr)||{};Object.keys(r).length!==0&&Object.keys(r).forEach(o=>{r[o]=G({},r[o],t[o]),delete t[o]}),this._instance.register({[Hr]:G({},r,t)}),n&&this._instance.reloadFeatureFlags()}resetGroupPropertiesForFlags(t){if(t){var n=this._instance.get_property(Hr)||{};this._instance.register({[Hr]:G({},n,{[t]:{}})})}else this._instance.unregister(Hr)}reset(){this.ei=!1,this.ri=!1,this.si=!1,this.ni=!1,this.oi=!1,this.ai=!1,this.$anon_distinct_id=void 0,this.di(),this.ii=!1}}var dO=["cookie","localstorage","localstorage+cookie","sessionstorage","memory"];class Zh{constructor(t,n){this.F=t,this.props={},this.mi=!1,this.yi=(r=>{var o="";return r.token&&(o=r.token.replace(/\+/g,"PL").replace(/\//g,"SL").replace(/=/g,"EQ")),r.persistence_name?"ph_"+r.persistence_name:"ph_"+o+"_posthog"})(t),this.Y=this.bi(t),this.load(),t.debug&&U.info("Persistence loaded",t.persistence,G({},this.props)),this.update_config(t,t,n),this.save()}isDisabled(){return!!this.wi}bi(t){dO.indexOf(t.persistence.toLowerCase())===-1&&(U.critical("Unknown persistence type "+t.persistence+"; falling back to localStorage+cookie"),t.persistence="localStorage+cookie");var n=t.persistence.toLowerCase();return n==="localstorage"&&Ge.H()?Ge:n==="localstorage+cookie"&&pc.H()?pc:n==="sessionstorage"&&mt.H()?mt:n==="memory"?w5:n==="cookie"?cn:pc.H()?pc:cn}properties(){var t={};return De(this.props,function(n,r){if(r===Yo&&ct(n))for(var o=Object.keys(n),a=0;a{this.props.hasOwnProperty(i)&&this.props[i]!==n||(this.props[i]=a,o=!0)}),o)return this.save(),!0}return!1}register(t,n){if(ct(t)){this.xi=L(n)?this.$i:n;var r=!1;if(De(t,(o,a)=>{t.hasOwnProperty(a)&&this.props[a]!==o&&(this.props[a]=o,r=!0)}),r)return this.save(),!0}return!1}unregister(t){t in this.props&&(delete this.props[t],this.save())}update_campaign_params(){if(!this.mi){var t=i2(this.F.custom_campaign_params,this.F.mask_personal_data_properties,this.F.custom_personal_data_properties);Go(Of(t))||this.register(t),this.mi=!0}}update_search_keyword(){var t;this.register((t=z==null?void 0:z.referrer)?c2(t):{})}update_referrer_info(){var t;this.register_once({$referrer:d2(),$referring_domain:z!=null&&z.referrer&&((t=Ld(z.referrer))==null?void 0:t.host)||"$direct"},void 0)}set_initial_person_info(){this.props[Rp]||this.props[Mp]||this.register_once({[Rd]:u2(this.F.mask_personal_data_properties,this.F.custom_personal_data_properties)},void 0)}get_initial_props(){var t={};De([Mp,Rp],i=>{var l=this.props[i];l&&De(l,function(c,d){t["$initial_"+_p(d)]=c})});var n,r,o=this.props[Rd];if(o){var a=(n=h2(o),r={},De(n,function(i,l){r["$initial_"+_p(l)]=i}),r);Je(t,a)}return t}safe_merge(t){return De(this.props,function(n,r){r in t||(t[r]=n)}),t}update_config(t,n,r){if(this.$i=this.xi=t.cookie_expiration,this.set_disabled(t.disable_persistence||!!r),this.set_cross_subdomain(t.cross_subdomain_cookie),this.set_secure(t.secure_cookie),t.persistence!==n.persistence){var o=this.bi(t),a=this.props;this.clear(),this.Y=o,this.props=a,this.save()}}set_disabled(t){this.wi=t,this.wi?this.remove():this.save()}set_cross_subdomain(t){t!==this.Si&&(this.Si=t,this.remove(),this.save())}set_secure(t){t!==this.Ei&&(this.Ei=t,this.remove(),this.save())}set_event_timer(t,n){var r=this.props[vi]||{};r[t]=n,this.props[vi]=r,this.save()}remove_event_timer(t){var n=(this.props[vi]||{})[t];return L(n)||(delete this.props[vi][t],this.save()),n}get_property(t){return this.props[t]}set_property(t,n){this.props[t]=n,this.save()}}var ui=function(e){return e.Activation="events",e.Cancellation="cancelEvents",e}({});(function(e){return e.Button="button",e.Tab="tab",e.Selector="selector",e})({});(function(e){return e.TopLeft="top_left",e.TopRight="top_right",e.TopCenter="top_center",e.MiddleLeft="middle_left",e.MiddleRight="middle_right",e.MiddleCenter="middle_center",e.Left="left",e.Center="center",e.Right="right",e.NextToTrigger="next_to_trigger",e})({});(function(e){return e.Top="top",e.Left="left",e.Right="right",e.Bottom="bottom",e})({});var em=function(e){return e.Popover="popover",e.API="api",e.Widget="widget",e.ExternalSurvey="external_survey",e}({});(function(e){return e.Open="open",e.MultipleChoice="multiple_choice",e.SingleChoice="single_choice",e.Rating="rating",e.Link="link",e})({});(function(e){return e.NextQuestion="next_question",e.End="end",e.ResponseBased="response_based",e.SpecificQuestion="specific_question",e})({});(function(e){return e.Once="once",e.Recurring="recurring",e.Always="always",e})({});var Wc=function(e){return e.SHOWN="survey shown",e.DISMISSED="survey dismissed",e.SENT="survey sent",e}({}),vb=function(e){return e.SURVEY_ID="$survey_id",e.SURVEY_NAME="$survey_name",e.SURVEY_RESPONSE="$survey_response",e.SURVEY_ITERATION="$survey_iteration",e.SURVEY_ITERATION_START_DATE="$survey_iteration_start_date",e.SURVEY_PARTIALLY_COMPLETED="$survey_partially_completed",e.SURVEY_SUBMISSION_ID="$survey_submission_id",e.SURVEY_QUESTIONS="$survey_questions",e.SURVEY_COMPLETED="$survey_completed",e}({}),w2=function(e){return e.Popover="popover",e.Inline="inline",e}({});class Uf{constructor(){this.ki={},this.ki={}}on(t,n){return this.ki[t]||(this.ki[t]=[]),this.ki[t].push(n),()=>{this.ki[t]=this.ki[t].filter(r=>r!==n)}}emit(t,n){for(var r of this.ki[t]||[])r(n);for(var o of this.ki["*"]||[])o(t,n)}}function Po(e,t,n){if(Ie(e))return!1;switch(n){case"exact":return e===t;case"contains":var r=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/_/g,".").replace(/%/g,".*");return new RegExp(r,"i").test(e);case"regex":try{return new RegExp(t).test(e)}catch{return!1}default:return!1}}class uO{constructor(t){this.Pi=new Uf,this.Ti=(n,r)=>this.Ri(n,r)&&this.Ii(n,r)&&this.Fi(n,r)&&this.Ci(n,r),this.Ri=(n,r)=>r==null||!r.event||(n==null?void 0:n.event)===(r==null?void 0:r.event),this._instance=t,this.Mi=new Set,this.Oi=new Set}init(){var t;if(!L((t=this._instance)==null?void 0:t.Ai)){var n;(n=this._instance)==null||n.Ai((r,o)=>{this.on(r,o)})}}register(t){var n,r;if(!L((n=this._instance)==null?void 0:n.Ai)&&(t.forEach(i=>{var l,c;(l=this.Oi)==null||l.add(i),(c=i.steps)==null||c.forEach(d=>{var u;(u=this.Mi)==null||u.add((d==null?void 0:d.event)||"")})}),(r=this._instance)!=null&&r.autocapture)){var o,a=new Set;t.forEach(i=>{var l;(l=i.steps)==null||l.forEach(c=>{c!=null&&c.selector&&a.add(c==null?void 0:c.selector)})}),(o=this._instance)==null||o.autocapture.setElementSelectors(a)}}on(t,n){var r;n!=null&&t.length!=0&&(this.Mi.has(t)||this.Mi.has(n==null?void 0:n.event))&&this.Oi&&((r=this.Oi)==null?void 0:r.size)>0&&this.Oi.forEach(o=>{this.Di(n,o)&&this.Pi.emit("actionCaptured",o.name)})}ji(t){this.onAction("actionCaptured",n=>t(n))}Di(t,n){if((n==null?void 0:n.steps)==null)return!1;for(var r of n.steps)if(this.Ti(t,r))return!0;return!1}onAction(t,n){return this.Pi.on(t,n)}Ii(t,n){if(n!=null&&n.url){var r,o=t==null||(r=t.properties)==null?void 0:r.$current_url;if(!o||typeof o!="string"||!Po(o,n.url,n.url_matching||"contains"))return!1}return!0}Fi(t,n){return!!this.Li(t,n)&&!!this.Ni(t,n)&&!!this.Ui(t,n)}Li(t,n){var r;if(n==null||!n.href)return!0;var o=this.zi(t);if(o.length>0)return o.some(l=>Po(l.href,n.href,n.href_matching||"exact"));var a,i=(t==null||(r=t.properties)==null?void 0:r.$elements_chain)||"";return!!i&&Po((a=i.match(/(?::|")href="proxy.php?url=https%3A%2F%2Foctomind.dev%2F%28.%2A%3F%29"/))?a[1]:"",n.href,n.href_matching||"exact")}Ni(t,n){var r;if(n==null||!n.text)return!0;var o=this.zi(t);if(o.length>0)return o.some(d=>Po(d.text,n.text,n.text_matching||"exact")||Po(d.$el_text,n.text,n.text_matching||"exact"));var a,i,l,c=(t==null||(r=t.properties)==null?void 0:r.$elements_chain)||"";return!!c&&(a=function(d){for(var u,h=[],p=/(?::|")text="(.*?)"/g;!Ie(u=p.exec(d));)h.includes(u[1])||h.push(u[1]);return h}(c),i=n.text,l=n.text_matching||"exact",a.some(d=>Po(d,i,l)))}Ui(t,n){var r,o;if(n==null||!n.selector)return!0;var a=t==null||(r=t.properties)==null?void 0:r.$element_selectors;if(a!=null&&a.includes(n.selector))return!0;var i=(t==null||(o=t.properties)==null?void 0:o.$elements_chain)||"";if(n.selector_regex&&i)try{return new RegExp(n.selector_regex).test(i)}catch{return!1}return!1}zi(t){var n;return(t==null||(n=t.properties)==null?void 0:n.$elements)==null?[]:t==null?void 0:t.properties.$elements}Ci(t,n){return n==null||!n.properties||n.properties.length===0||b2(n.properties.reduce((r,o)=>{var a=Ae(o.value)?o.value.map(String):o.value!=null?[String(o.value)]:[];return r[o.key]={values:a,operator:o.operator||"exact"},r},{}),t==null?void 0:t.properties)}}var Ce=it("[Surveys]"),Hp="seenSurvey_",hO=(e,t)=>{var n="$survey_"+t+"/"+e.id;return e.current_iteration&&e.current_iteration>0&&(n="$survey_"+t+"/"+e.id+"/"+e.current_iteration),n},yb=e=>{var t=""+Hp+e.id;return e.current_iteration&&e.current_iteration>0&&(t=""+Hp+e.id+"_"+e.current_iteration),t},mO=[em.Popover,em.Widget,em.API],pO={ignoreConditions:!1,ignoreDelay:!1,displayType:w2.Popover};class xO{constructor(t){this._instance=t,this.Hi=new Map,this.Bi=new Map,this.qi=new Map}Wi(t,n){return!!t&&b2(t.propertyFilters,n==null?void 0:n.properties)}Gi(t,n){var r=new Map;return t.forEach(o=>{var a;(a=o.conditions)==null||(a=a[n])==null||(a=a.values)==null||a.forEach(i=>{if(i!=null&&i.name){var l=r.get(i.name)||[];l.push(o.id),r.set(i.name,l)}})}),r}Vi(t,n,r){var o,a=(r===ui.Activation?this.Hi:this.Bi).get(t),i=[];return(o=this._instance)==null||o.getSurveys(l=>{i=l.filter(c=>a==null?void 0:a.includes(c.id))}),i.filter(l=>{var c,d=(c=l.conditions)==null||(c=c[r])==null||(c=c.values)==null?void 0:c.find(u=>u.name===t);return this.Wi(d,n)})}register(t){var n;L((n=this._instance)==null?void 0:n.Ai)||(this.Ji(t),this.Ki(t))}Ki(t){var n=t.filter(r=>{var o,a;return((o=r.conditions)==null?void 0:o.actions)&&((a=r.conditions)==null||(a=a.actions)==null||(a=a.values)==null?void 0:a.length)>0});n.length!==0&&(this.Yi==null&&(this.Yi=new uO(this._instance),this.Yi.init(),this.Yi.ji(r=>{this.onAction(r)})),n.forEach(r=>{var o,a,i,l,c;r.conditions&&(o=r.conditions)!=null&&o.actions&&(a=r.conditions)!=null&&(a=a.actions)!=null&&a.values&&((i=r.conditions)==null||(i=i.actions)==null||(i=i.values)==null?void 0:i.length)>0&&((l=this.Yi)==null||l.register(r.conditions.actions.values),(c=r.conditions)==null||(c=c.actions)==null||(c=c.values)==null||c.forEach(d=>{if(d&&d.name){var u=this.qi.get(d.name);u&&u.push(r.id),this.qi.set(d.name,u||[r.id])}}))}))}Ji(t){var n,r=t.filter(a=>{var i,l;return((i=a.conditions)==null?void 0:i.events)&&((l=a.conditions)==null||(l=l.events)==null||(l=l.values)==null?void 0:l.length)>0}),o=t.filter(a=>{var i,l;return((i=a.conditions)==null?void 0:i.cancelEvents)&&((l=a.conditions)==null||(l=l.cancelEvents)==null||(l=l.values)==null?void 0:l.length)>0});(r.length!==0||o.length!==0)&&((n=this._instance)==null||n.Ai((a,i)=>{this.onEvent(a,i)}),this.Hi=this.Gi(t,ui.Activation),this.Bi=this.Gi(t,ui.Cancellation))}onEvent(t,n){var r,o=((r=this._instance)==null||(r=r.persistence)==null?void 0:r.props[mc])||[];if(Wc.SHOWN===t&&n&&o.length>0){var a;Ce.info("survey event matched, removing survey from activated surveys",{event:t,eventPayload:n,existingActivatedSurveys:o});var i=n==null||(a=n.properties)==null?void 0:a.$survey_id;if(i){var l=o.indexOf(i);l>=0&&(o.splice(l,1),this.Xi(o))}}else{if(this.Bi.has(t)){var c=this.Vi(t,n,ui.Cancellation);c.length>0&&(Ce.info("cancel event matched, cancelling surveys",{event:t,surveysToCancel:c.map(u=>u.id)}),c.forEach(u=>{var h,p=o.indexOf(u.id);p>=0&&o.splice(p,1),(h=this._instance)==null||h.cancelPendingSurvey(u.id)}),this.Xi(o))}if(this.Hi.has(t)){Ce.info("survey event name matched",{event:t,eventPayload:n,surveys:this.Hi.get(t)});var d=this.Vi(t,n,ui.Activation);this.Xi(o.concat(d.map(u=>u.id)||[]))}}}onAction(t){var n,r=((n=this._instance)==null||(n=n.persistence)==null?void 0:n.props[mc])||[];this.qi.has(t)&&this.Xi(r.concat(this.qi.get(t)||[]))}Xi(t){var n;Ce.info("updating activated surveys",{activatedSurveys:t}),(n=this._instance)==null||(n=n.persistence)==null||n.register({[mc]:[...new Set(t)]})}getSurveys(){var t,n=(t=this._instance)==null||(t=t.persistence)==null?void 0:t.props[mc];return n||[]}getEventToSurveys(){return this.Hi}Qi(){return this.Yi}}class fO{constructor(t){this.Zi=void 0,this._surveyManager=null,this.te=!1,this.ie=!1,this.ee=[],this._instance=t,this._surveyEventReceiver=null}onRemoteConfig(t){if(!this._instance.config.disable_surveys){var n=t.surveys;if(Ie(n))return Ce.warn("Flags not loaded yet. Not loading surveys.");var r=Ae(n);this.Zi=r?n.length>0:n,Ce.info("flags response received, isSurveysEnabled: "+this.Zi),this.loadIfEnabled()}}reset(){localStorage.removeItem("lastSeenSurveyDate");for(var t=[],n=0;nlocalStorage.removeItem(o))}loadIfEnabled(){if(!this._surveyManager)if(this.ie)Ce.info("Already initializing surveys, skipping...");else if(this._instance.config.disable_surveys)Ce.info("Disabled. Not loading surveys.");else if(this._instance.config.cookieless_mode&&this._instance.consent.isOptedOut())Ce.info("Not loading surveys in cookieless mode without consent.");else{var t=ne==null?void 0:ne.__PosthogExtensions__;if(t){if(!L(this.Zi)||this._instance.config.advanced_enable_surveys){var n=this.Zi||this._instance.config.advanced_enable_surveys;this.ie=!0;try{var r=t.generateSurveys;if(r)return void this.re(r,n);var o=t.loadExternalDependency;if(!o)return void this.se("PostHog loadExternalDependency extension not found.");o(this._instance,"surveys",a=>{a||!t.generateSurveys?this.se("Could not load surveys script",a):this.re(t.generateSurveys,n)})}catch(a){throw this.se("Error initializing surveys",a),a}finally{this.ie=!1}}}else Ce.error("PostHog Extensions not found.")}}re(t,n){this._surveyManager=t(this._instance,n),this._surveyEventReceiver=new xO(this._instance),Ce.info("Surveys loaded successfully"),this.ne({isLoaded:!0})}se(t,n){Ce.error(t,n),this.ne({isLoaded:!1,error:t})}onSurveysLoaded(t){return this.ee.push(t),this._surveyManager&&this.ne({isLoaded:!0}),()=>{this.ee=this.ee.filter(n=>n!==t)}}getSurveys(t,n){if(n===void 0&&(n=!1),this._instance.config.disable_surveys)return Ce.info("Disabled. Not loading surveys."),t([]);var r=this._instance.get_property(Pp);if(r&&!n)return t(r,{isLoaded:!0});if(this.te)return t([],{isLoaded:!1,error:"Surveys are already being loaded"});try{this.te=!0,this._instance.ci({url:this._instance.requestRouter.endpointFor("api","/api/surveys/?token="+this._instance.config.token),method:"GET",timeout:this._instance.config.surveys_request_timeout_ms,callback:o=>{var a;this.te=!1;var i=o.statusCode;if(i!==200||!o.json){var l="Surveys API could not be loaded, status: "+i;return Ce.error(l),t([],{isLoaded:!1,error:l})}var c,d=o.json.surveys||[],u=d.filter(h=>function(p){return!(!p.start_date||p.end_date)}(h)&&(function(p){var m;return!((m=p.conditions)==null||(m=m.events)==null||(m=m.values)==null||!m.length)}(h)||function(p){var m;return!((m=p.conditions)==null||(m=m.actions)==null||(m=m.values)==null||!m.length)}(h)));return u.length>0&&((c=this._surveyEventReceiver)==null||c.register(u)),(a=this._instance.persistence)==null||a.register({[Pp]:d}),t(d,{isLoaded:!0})}})}catch(o){throw this.te=!1,o}}ne(t){for(var n of this.ee)try{if(!t.isLoaded)return n([],t);this.getSurveys(n)}catch(r){Ce.error("Error in survey callback",r)}}getActiveMatchingSurveys(t,n){if(n===void 0&&(n=!1),!Ie(this._surveyManager))return this._surveyManager.getActiveMatchingSurveys(t,n);Ce.warn("init was not called")}oe(t){var n=null;return this.getSurveys(r=>{var o;n=(o=r.find(a=>a.id===t))!==null&&o!==void 0?o:null}),n}ae(t){if(Ie(this._surveyManager))return{eligible:!1,reason:"SDK is not enabled or survey functionality is not yet loaded"};var n=typeof t=="string"?this.oe(t):t;return n?this._surveyManager.checkSurveyEligibility(n):{eligible:!1,reason:"Survey not found"}}canRenderSurvey(t){if(Ie(this._surveyManager))return Ce.warn("init was not called"),{visible:!1,disabledReason:"SDK is not enabled or survey functionality is not yet loaded"};var n=this.ae(t);return{visible:n.eligible,disabledReason:n.reason}}canRenderSurveyAsync(t,n){return Ie(this._surveyManager)?(Ce.warn("init was not called"),Promise.resolve({visible:!1,disabledReason:"SDK is not enabled or survey functionality is not yet loaded"})):new Promise(r=>{this.getSurveys(o=>{var a,i=(a=o.find(c=>c.id===t))!==null&&a!==void 0?a:null;if(i){var l=this.ae(i);r({visible:l.eligible,disabledReason:l.reason})}else r({visible:!1,disabledReason:"Survey not found"})},n)})}renderSurvey(t,n){var r;if(Ie(this._surveyManager))Ce.warn("init was not called");else{var o=typeof t=="string"?this.oe(t):t;if(o!=null&&o.id)if(mO.includes(o.type)){var a=z==null?void 0:z.querySelector(n);if(a)return(r=o.appearance)!=null&&r.surveyPopupDelaySeconds?(Ce.info("Rendering survey "+o.id+" with delay of "+o.appearance.surveyPopupDelaySeconds+" seconds"),void setTimeout(()=>{var i,l;Ce.info("Rendering survey "+o.id+" with delay of "+((i=o.appearance)==null?void 0:i.surveyPopupDelaySeconds)+" seconds"),(l=this._surveyManager)==null||l.renderSurvey(o,a),Ce.info("Survey "+o.id+" rendered")},1e3*o.appearance.surveyPopupDelaySeconds)):void this._surveyManager.renderSurvey(o,a);Ce.warn("Survey element not found")}else Ce.warn("Surveys of type "+o.type+" cannot be rendered in the app");else Ce.warn("Survey not found")}}displaySurvey(t,n){var r;if(Ie(this._surveyManager))Ce.warn("init was not called");else{var o=this.oe(t);if(o){var a=o;if((r=o.appearance)!=null&&r.surveyPopupDelaySeconds&&n.ignoreDelay&&(a=G({},o,{appearance:G({},o.appearance,{surveyPopupDelaySeconds:0})})),n.ignoreConditions===!1){var i=this.canRenderSurvey(o);if(!i.visible)return void Ce.warn("Survey is not eligible to be displayed: ",i.disabledReason)}n.displayType!==w2.Inline?this._surveyManager.handlePopoverSurvey(a):this.renderSurvey(a,n.selector)}else Ce.warn("Survey not found")}}cancelPendingSurvey(t){Ie(this._surveyManager)?Ce.warn("init was not called"):this._surveyManager.cancelSurvey(t)}}var bb=it("[RateLimiter]");class gO{constructor(t){var n,r;this.serverLimits={},this.lastEventRateLimited=!1,this.checkForLimiting=o=>{var a=o.text;if(a&&a.length)try{(JSON.parse(a).quota_limited||[]).forEach(i=>{bb.info((i||"events")+" is quota limited."),this.serverLimits[i]=new Date().getTime()+6e4})}catch(i){return void bb.warn('could not rate limit - continuing. Error: "'+(i==null?void 0:i.message)+'"',{text:a})}},this.instance=t,this.captureEventsPerSecond=((n=t.config.rate_limiting)==null?void 0:n.events_per_second)||10,this.captureEventsBurstLimit=Math.max(((r=t.config.rate_limiting)==null?void 0:r.events_burst_limit)||10*this.captureEventsPerSecond,this.captureEventsPerSecond),this.lastEventRateLimited=this.clientRateLimitContext(!0).isRateLimited}clientRateLimitContext(t){var n,r,o;t===void 0&&(t=!1);var a=new Date().getTime(),i=(n=(r=this.instance.persistence)==null?void 0:r.get_property(Dp))!==null&&n!==void 0?n:{tokens:this.captureEventsBurstLimit,last:a};i.tokens+=(a-i.last)/1e3*this.captureEventsPerSecond,i.last=a,i.tokens>this.captureEventsBurstLimit&&(i.tokens=this.captureEventsBurstLimit);var l=i.tokens<1;return l||t||(i.tokens=Math.max(0,i.tokens-1)),!l||this.lastEventRateLimited||t||this.instance.capture("$$client_ingestion_warning",{$$client_ingestion_warning_message:"posthog-js client rate limited. Config is set to "+this.captureEventsPerSecond+" events per second and "+this.captureEventsBurstLimit+" events burst limit."},{skip_client_rate_limiting:!0}),this.lastEventRateLimited=l,(o=this.instance.persistence)==null||o.set_property(Dp,i),{isRateLimited:l,remainingTokens:i.tokens}}isServerRateLimited(t){var n=this.serverLimits[t||"events"]||!1;return n!==!1&&new Date().getTime()t(this.remoteConfig)):(Br.error("PostHog Extensions not found. Cannot load remote config."),t())}ue(t){this._instance.ci({method:"GET",url:this._instance.requestRouter.endpointFor("assets","/array/"+this._instance.config.token+"/config"),callback:n=>{t(n.json)}})}load(){try{if(this.remoteConfig)return Br.info("Using preloaded remote config",this.remoteConfig),void this.fi(this.remoteConfig);if(this._instance.O())return void Br.warn("Remote config is disabled. Falling back to local config.");this.le(t=>{if(!t)return Br.info("No config found after loading remote JS config. Falling back to JSON."),void this.ue(n=>{this.fi(n)});this.fi(t)})}catch(t){Br.error("Error loading remote config",t)}}fi(t){t?this._instance.config.__preview_remote_config?(this._instance.fi(t),t.hasFeatureFlags!==!1&&this._instance.featureFlags.ensureFlagsLoaded()):Br.info("__preview_remote_config is disabled. Logging config instead",t):Br.error("Failed to fetch remote config from PostHog.")}}var qp=3e3;class yO{constructor(t,n){this.he=!0,this.ve=[],this.de=nn((n==null?void 0:n.flush_interval_ms)||qp,250,5e3,U.createLogger("flush interval"),qp),this.ce=t}enqueue(t){this.ve.push(t),this.fe||this.pe()}unload(){this.ge();var t=this.ve.length>0?this._e():{},n=Object.values(t);[...n.filter(r=>r.url.indexOf("/e")===0),...n.filter(r=>r.url.indexOf("/e")!==0)].map(r=>{this.ce(G({},r,{transport:"sendBeacon"}))})}enable(){this.he=!1,this.pe()}pe(){var t=this;this.he||(this.fe=setTimeout(()=>{if(this.ge(),this.ve.length>0){var n=this._e(),r=function(){var a=n[o],i=new Date().getTime();a.data&&Ae(a.data)&&De(a.data,l=>{l.offset=Math.abs(l.timestamp-i),delete l.timestamp}),t.ce(a)};for(var o in n)r()}},this.de))}ge(){clearTimeout(this.fe),this.fe=void 0}_e(){var t={};return De(this.ve,n=>{var r,o=n,a=(o?o.batchKey:null)||o.url;L(t[a])&&(t[a]=G({},o,{data:[]})),(r=t[a].data)==null||r.push(o.data)}),this.ve=[],t}}var bO=["retriesPerformedSoFar"];class wO{constructor(t){this.me=!1,this.ye=3e3,this.ve=[],this._instance=t,this.ve=[],this.be=!0,!L(S)&&"onLine"in S.navigator&&(this.be=S.navigator.onLine,this.we=()=>{this.be=!0,this.Dt()},this.xe=()=>{this.be=!1},nt(S,"online",this.we),nt(S,"offline",this.xe))}get length(){return this.ve.length}retriableRequest(t){var{retriesPerformedSoFar:n}=t,r=fk(t,bO);un(n)&&n>0&&(r.url=Hd(r.url,{retry_count:n})),this._instance.ci(G({},r,{callback:o=>{o.statusCode!==200&&(o.statusCode<400||o.statusCode>=500)&&(n??0)<10?this.Se(G({retriesPerformedSoFar:n},r)):r.callback==null||r.callback(o)}}))}Se(t){var n=t.retriesPerformedSoFar||0;t.retriesPerformedSoFar=n+1;var r=function(i){var l=3e3*Math.pow(2,i),c=l/2,d=Math.min(18e5,l),u=(Math.random()-.5)*(d-c);return Math.ceil(d+u)}(n),o=Date.now()+r;this.ve.push({retryAt:o,requestOptions:t});var a="Enqueued failed request for retry in "+r;navigator.onLine||(a+=" (Browser is offline)"),U.warn(a),this.me||(this.me=!0,this.Ee())}Ee(){if(this.$e&&clearTimeout(this.$e),this.ve.length===0)return this.me=!1,void(this.$e=void 0);this.$e=setTimeout(()=>{this.be&&this.ve.length>0&&this.Dt(),this.Ee()},this.ye)}Dt(){var t=Date.now(),n=[],r=this.ve.filter(a=>a.retryAt0)for(var{requestOptions:o}of r)this.retriableRequest(o)}unload(){for(var{requestOptions:t}of(this.$e&&(clearTimeout(this.$e),this.$e=void 0),this.me=!1,L(S)||(this.we&&(S.removeEventListener("online",this.we),this.we=void 0),this.xe&&(S.removeEventListener("offline",this.xe),this.xe=void 0)),this.ve))try{this._instance.ci(G({},t,{transport:"sendBeacon"}))}catch(n){U.error(n)}this.ve=[]}}class jO{constructor(t){this.ke=()=>{var n,r,o,a;this.Pe||(this.Pe={});var i=this.scrollElement(),l=this.scrollY(),c=i?Math.max(0,i.scrollHeight-i.clientHeight):0,d=l+((i==null?void 0:i.clientHeight)||0),u=(i==null?void 0:i.scrollHeight)||0;this.Pe.lastScrollY=Math.ceil(l),this.Pe.maxScrollY=Math.max(l,(n=this.Pe.maxScrollY)!==null&&n!==void 0?n:0),this.Pe.maxScrollHeight=Math.max(c,(r=this.Pe.maxScrollHeight)!==null&&r!==void 0?r:0),this.Pe.lastContentY=d,this.Pe.maxContentY=Math.max(d,(o=this.Pe.maxContentY)!==null&&o!==void 0?o:0),this.Pe.maxContentHeight=Math.max(u,(a=this.Pe.maxContentHeight)!==null&&a!==void 0?a:0)},this._instance=t}getContext(){return this.Pe}resetContext(){var t=this.Pe;return setTimeout(this.ke,0),t}startMeasuringScrollPosition(){nt(S,"scroll",this.ke,{capture:!0}),nt(S,"scrollend",this.ke,{capture:!0}),nt(S,"resize",this.ke)}scrollElement(){if(!this._instance.config.scroll_root_selector)return S==null?void 0:S.document.documentElement;var t=Ae(this._instance.config.scroll_root_selector)?this._instance.config.scroll_root_selector:[this._instance.config.scroll_root_selector];for(var n of t){var r=S==null?void 0:S.document.querySelector(n);if(r)return r}}scrollY(){if(this._instance.config.scroll_root_selector){var t=this.scrollElement();return t&&t.scrollTop||0}return S&&(S.scrollY||S.pageYOffset||S.document.documentElement.scrollTop)||0}scrollX(){if(this._instance.config.scroll_root_selector){var t=this.scrollElement();return t&&t.scrollLeft||0}return S&&(S.scrollX||S.pageXOffset||S.document.documentElement.scrollLeft)||0}}var NO=e=>u2(e==null?void 0:e.config.mask_personal_data_properties,e==null?void 0:e.config.custom_personal_data_properties);class wb{constructor(t,n,r,o){this.Te=a=>{var i=this.Re();if(!i||i.sessionId!==a){var l={sessionId:a,props:this.Ie(this._instance)};this.Fe.register({[Op]:l})}},this._instance=t,this.Ce=n,this.Fe=r,this.Ie=o||NO,this.Ce.onSessionId(this.Te)}Re(){return this.Fe.props[Op]}getSetOnceProps(){var t,n=(t=this.Re())==null?void 0:t.props;return n?"r"in n?h2(n):{$referring_domain:n.referringDomain,$pathname:n.initialPathName,utm_source:n.utm_source,utm_campaign:n.utm_campaign,utm_medium:n.utm_medium,utm_content:n.utm_content,utm_term:n.utm_term}:{}}getSessionProps(){var t={};return De(Of(this.getSetOnceProps()),(n,r)=>{r==="$current_url"&&(r="url"),t["$session_entry_"+_p(r)]=n}),t}}var tm=it("[SessionId]");class jb{on(t,n){return this.Me.on(t,n)}constructor(t,n,r){var o;if(this.Oe=[],this.Ae=void 0,this.Me=new Uf,this.De=(u,h)=>Math.abs(u-h)>this.sessionTimeoutMs,!t.persistence)throw new Error("SessionIdManager requires a PostHogPersistence instance");if(t.config.cookieless_mode==="always")throw new Error('SessionIdManager cannot be used with cookieless_mode="always"');this.F=t.config,this.Fe=t.persistence,this.je=void 0,this.Le=void 0,this._sessionStartTimestamp=null,this._sessionActivityTimestamp=null,this.Ne=n||ur,this.Ue=r||ur;var a=this.F.persistence_name||this.F.token,i=this.F.session_idle_timeout_seconds||1800;if(this._sessionTimeoutMs=1e3*nn(i,60,36e3,tm.createLogger("session_idle_timeout_seconds"),1800),t.register({$configured_session_timeout_ms:this._sessionTimeoutMs}),this.ze(),this.He="ph_"+a+"_window_id",this.Be="ph_"+a+"_primary_window_exists",this.qe()){var l=mt.W(this.He),c=mt.W(this.Be);l&&!c?this.je=l:mt.V(this.He),mt.G(this.Be,!0)}if((o=this.F.bootstrap)!=null&&o.sessionID)try{var d=(u=>{var h=u.replace(/-/g,"");if(h.length!==32)throw new Error("Not a valid UUID");if(h[12]!=="7")throw new Error("Not a UUIDv7");return parseInt(h.substring(0,12),16)})(this.F.bootstrap.sessionID);this.We(this.F.bootstrap.sessionID,new Date().getTime(),d)}catch(u){tm.error("Invalid sessionID in bootstrap",u)}this.Ge()}get sessionTimeoutMs(){return this._sessionTimeoutMs}onSessionId(t){return L(this.Oe)&&(this.Oe=[]),this.Oe.push(t),this.Le&&t(this.Le,this.je),()=>{this.Oe=this.Oe.filter(n=>n!==t)}}qe(){return this.F.persistence!=="memory"&&!this.Fe.wi&&mt.H()}Ve(t){t!==this.je&&(this.je=t,this.qe()&&mt.G(this.He,t))}Je(){return this.je?this.je:this.qe()?mt.W(this.He):null}We(t,n,r){t===this.Le&&n===this._sessionActivityTimestamp&&r===this._sessionStartTimestamp||(this._sessionStartTimestamp=r,this._sessionActivityTimestamp=n,this.Le=t,this.Fe.register({[Od]:[n,t,r]}))}Ke(){var t=this.Fe.props[Od];return Ae(t)&&t.length===2&&t.push(t[0]),t||[0,null,0]}resetSessionId(){this.We(null,null,null)}destroy(){clearTimeout(this.Ye),this.Ye=void 0,this.Ae&&S&&(S.removeEventListener("beforeunload",this.Ae,{capture:!1}),this.Ae=void 0),this.Oe=[]}Ge(){this.Ae=()=>{this.qe()&&mt.V(this.Be)},nt(S,"beforeunload",this.Ae,{capture:!1})}checkAndGetSessionAndWindowId(t,n){if(t===void 0&&(t=!1),n===void 0&&(n=null),this.F.cookieless_mode==="always")throw new Error('checkAndGetSessionAndWindowId should not be called with cookieless_mode="always"');var r=n||new Date().getTime(),[o,a,i]=this.Ke(),l=this.Je(),c=un(i)&&i>0&&Math.abs(r-i)>864e5,d=!1,u=!a,h=!u&&!t&&this.De(r,o);u||h||c?(a=this.Ne(),l=this.Ue(),tm.info("new session ID generated",{sessionId:a,windowId:l,changeReason:{noSessionId:u,activityTimeout:h,sessionPastMaximumLength:c}}),i=r,d=!0):l||(l=this.Ue(),d=!0);var p=o===0||!t||c?r:o,m=i===0?new Date().getTime():i;return this.Ve(l),this.We(a,p,m),t||this.ze(),d&&this.Oe.forEach(w=>w(a,l,d?{noSessionId:u,activityTimeout:h,sessionPastMaximumLength:c}:void 0)),{sessionId:a,windowId:l,sessionStartTimestamp:m,changeReason:d?{noSessionId:u,activityTimeout:h,sessionPastMaximumLength:c}:void 0,lastActivityTimestamp:o}}ze(){clearTimeout(this.Ye),this.Ye=setTimeout(()=>{var[t]=this.Ke();if(this.De(new Date().getTime(),t)){var n=this.Le;this.resetSessionId(),this.Me.emit("forcedIdleReset",{idleSessionId:n})}},1.1*this.sessionTimeoutMs)}}var kO=["$set_once","$set"],Yn=it("[SiteApps]");class _O{constructor(t){this._instance=t,this.Xe=[],this.apps={}}get isEnabled(){return!!this._instance.config.opt_in_site_apps}Qe(t,n){if(n){var r=this.globalsForEvent(n);this.Xe.push(r),this.Xe.length>1e3&&(this.Xe=this.Xe.slice(10))}}get siteAppLoaders(){var t;return(t=ne._POSTHOG_REMOTE_CONFIG)==null||(t=t[this._instance.config.token])==null?void 0:t.siteApps}init(){if(this.isEnabled){var t=this._instance.Ai(this.Qe.bind(this));this.Ze=()=>{t(),this.Xe=[],this.Ze=void 0}}}globalsForEvent(t){var n,r,o,a,i,l,c;if(!t)throw new Error("Event payload is required");var d={},u=this._instance.get_property("$groups")||[],h=this._instance.get_property("$stored_group_properties")||{};for(var[p,m]of Object.entries(h))d[p]={id:u[p],type:p,properties:m};var{$set_once:w,$set:x}=t;return{event:G({},fk(t,kO),{properties:G({},t.properties,x?{$set:G({},(n=(r=t.properties)==null?void 0:r.$set)!==null&&n!==void 0?n:{},x)}:{},w?{$set_once:G({},(o=(a=t.properties)==null?void 0:a.$set_once)!==null&&o!==void 0?o:{},w)}:{}),elements_chain:(i=(l=t.properties)==null?void 0:l.$elements_chain)!==null&&i!==void 0?i:"",distinct_id:(c=t.properties)==null?void 0:c.distinct_id}),person:{properties:this._instance.get_property("$stored_person_properties")},groups:d}}setupSiteApp(t){var n=this.apps[t.id],r=()=>{var l;!n.errored&&this.Xe.length&&(Yn.info("Processing "+this.Xe.length+" events for site app with id "+t.id),this.Xe.forEach(c=>n.processEvent==null?void 0:n.processEvent(c)),n.processedBuffer=!0),Object.values(this.apps).every(c=>c.processedBuffer||c.errored)&&((l=this.Ze)==null||l.call(this))},o=!1,a=l=>{n.errored=!l,n.loaded=!0,Yn.info("Site app with id "+t.id+" "+(l?"loaded":"errored")),o&&r()};try{var{processEvent:i}=t.init({posthog:this._instance,callback:l=>{a(l)}});i&&(n.processEvent=i),o=!0}catch(l){Yn.error("Error while initializing PostHog app with config id "+t.id,l),a(!1)}if(o&&n.loaded)try{r()}catch(l){Yn.error("Error while processing buffered events PostHog app with config id "+t.id,l),n.errored=!0}}tr(){var t=this.siteAppLoaders||[];for(var n of t)this.apps[n.id]={id:n.id,loaded:!1,errored:!1,processedBuffer:!1};for(var r of t)this.setupSiteApp(r)}ir(t){if(Object.keys(this.apps).length!==0){var n=this.globalsForEvent(t);for(var r of Object.values(this.apps))try{r.processEvent==null||r.processEvent(n)}catch(o){Yn.error("Error while processing event "+t.event+" for site app "+r.id,o)}}}onRemoteConfig(t){var n,r,o,a=this;if((n=this.siteAppLoaders)!=null&&n.length)return this.isEnabled?(this.tr(),void this._instance.on("eventCaptured",d=>this.ir(d))):void Yn.error('PostHog site apps are disabled. Enable the "opt_in_site_apps" config to proceed.');if((r=this.Ze)==null||r.call(this),(o=t.siteApps)!=null&&o.length)if(this.isEnabled){var i=function(d){var u;ne["__$$ph_site_app_"+d]=a._instance,(u=ne.__PosthogExtensions__)==null||u.loadSiteApp==null||u.loadSiteApp(a._instance,c,h=>{if(h)return Yn.error("Error while initializing PostHog app with config id "+d,h)})};for(var{id:l,url:c}of t.siteApps)i(l)}else Yn.error('PostHog site apps are disabled. Enable the "opt_in_site_apps" config to proceed.')}}var j2=function(e,t){if(!e)return!1;var n=e.userAgent;if(n&&Dy(n,t))return!0;try{var r=e==null?void 0:e.userAgentData;if(r!=null&&r.brands&&r.brands.some(o=>Dy(o==null?void 0:o.brand,t)))return!0}catch{}return!!e.webdriver},Ni=function(e){return e.US="us",e.EU="eu",e.CUSTOM="custom",e}({}),Nb="i.posthog.com";class CO{constructor(t){this.er={},this.instance=t}get apiHost(){var t=this.instance.config.api_host.trim().replace(/\/$/,"");return t==="https://app.posthog.com"?"https://us.i.posthog.com":t}get flagsApiHost(){var t=this.instance.config.flags_api_host;return t?t.trim().replace(/\/$/,""):this.apiHost}get uiHost(){var t,n=(t=this.instance.config.ui_host)==null?void 0:t.replace(/\/$/,"");return n||(n=this.apiHost.replace("."+Nb,".posthog.com")),n==="https://app.posthog.com"?"https://us.posthog.com":n}get region(){return this.er[this.apiHost]||(/https:\/\/(app|us|us-assets)(\.i)?\.posthog\.com/i.test(this.apiHost)?this.er[this.apiHost]=Ni.US:/https:\/\/(eu|eu-assets)(\.i)?\.posthog\.com/i.test(this.apiHost)?this.er[this.apiHost]=Ni.EU:this.er[this.apiHost]=Ni.CUSTOM),this.er[this.apiHost]}endpointFor(t,n){if(n===void 0&&(n=""),n&&(n=n[0]==="/"?n:"/"+n),t==="ui")return this.uiHost+n;if(t==="flags")return this.flagsApiHost+n;if(this.region===Ni.CUSTOM)return this.apiHost+n;var r=Nb+n;switch(t){case"assets":return"https://"+this.region+"-assets."+r;case"api":return"https://"+this.region+"."+r}}}var SO={icontains:(e,t)=>!!S&&t.href.toLowerCase().indexOf(e.toLowerCase())>-1,not_icontains:(e,t)=>!!S&&t.href.toLowerCase().indexOf(e.toLowerCase())===-1,regex:(e,t)=>!!S&&qd(t.href,e),not_regex:(e,t)=>!!S&&!qd(t.href,e),exact:(e,t)=>t.href===e,is_not:(e,t)=>t.href!==e};class kt{constructor(t){var n=this;this.getWebExperimentsAndEvaluateDisplayLogic=function(r){r===void 0&&(r=!1),n.getWebExperiments(o=>{kt.rr("retrieved web experiments from the server"),n.sr=new Map,o.forEach(a=>{if(a.feature_flag_key){var i;n.sr&&(kt.rr("setting flag key ",a.feature_flag_key," to web experiment ",a),(i=n.sr)==null||i.set(a.feature_flag_key,a));var l=n._instance.getFeatureFlag(a.feature_flag_key);ot(l)&&a.variants[l]&&n.nr(a.name,l,a.variants[l].transforms)}else if(a.variants)for(var c in a.variants){var d=a.variants[c];kt.ar(d)&&n.nr(a.name,c,d.transforms)}})},r)},this._instance=t,this._instance.onFeatureFlags(r=>{this.onFeatureFlags(r)})}onFeatureFlags(t){if(this._is_bot())kt.rr("Refusing to render web experiment since the viewer is a likely bot");else if(!this._instance.config.disable_web_experiments){if(Ie(this.sr))return this.sr=new Map,this.loadIfEnabled(),void this.previewWebExperiment();kt.rr("applying feature flags",t),t.forEach(n=>{var r;if(this.sr&&(r=this.sr)!=null&&r.has(n)){var o,a=this._instance.getFeatureFlag(n),i=(o=this.sr)==null?void 0:o.get(n);a&&i!=null&&i.variants[a]&&this.nr(i.name,a,i.variants[a].transforms)}})}}previewWebExperiment(){var t=kt.getWindowLocation();if(t!=null&&t.search){var n=Fd(t==null?void 0:t.search,"__experiment_id"),r=Fd(t==null?void 0:t.search,"__experiment_variant");n&&r&&(kt.rr("previewing web experiments "+n+" && "+r),this.getWebExperiments(o=>{this.lr(parseInt(n),r,o)},!1,!0))}}loadIfEnabled(){this._instance.config.disable_web_experiments||this.getWebExperimentsAndEvaluateDisplayLogic()}getWebExperiments(t,n,r){if(this._instance.config.disable_web_experiments&&!r)return t([]);var o=this._instance.get_property("$web_experiments");if(o&&!n)return t(o);this._instance.ci({url:this._instance.requestRouter.endpointFor("api","/api/web_experiments/?token="+this._instance.config.token),method:"GET",callback:a=>{if(a.statusCode!==200||!a.json)return t([]);var i=a.json.experiments||[];return t(i)}})}lr(t,n,r){var o=r.filter(a=>a.id===t);o&&o.length>0&&(kt.rr("Previewing web experiment ["+o[0].name+"] with variant ["+n+"]"),this.nr(o[0].name,n,o[0].variants[n].transforms))}static ar(t){return!Ie(t.conditions)&&kt.ur(t)&&kt.hr(t)}static ur(t){var n;if(Ie(t.conditions)||Ie((n=t.conditions)==null?void 0:n.url))return!0;var r,o,a,i=kt.getWindowLocation();return!!i&&((r=t.conditions)==null||!r.url||SO[(o=(a=t.conditions)==null?void 0:a.urlMatchType)!==null&&o!==void 0?o:"icontains"](t.conditions.url,i))}static getWindowLocation(){return S==null?void 0:S.location}static hr(t){var n;if(Ie(t.conditions)||Ie((n=t.conditions)==null?void 0:n.utm))return!0;var r=i2();if(r.utm_source){var o,a,i,l,c,d,u,h,p=(o=t.conditions)==null||(o=o.utm)==null||!o.utm_campaign||((a=t.conditions)==null||(a=a.utm)==null?void 0:a.utm_campaign)==r.utm_campaign,m=(i=t.conditions)==null||(i=i.utm)==null||!i.utm_source||((l=t.conditions)==null||(l=l.utm)==null?void 0:l.utm_source)==r.utm_source,w=(c=t.conditions)==null||(c=c.utm)==null||!c.utm_medium||((d=t.conditions)==null||(d=d.utm)==null?void 0:d.utm_medium)==r.utm_medium,x=(u=t.conditions)==null||(u=u.utm)==null||!u.utm_term||((h=t.conditions)==null||(h=h.utm)==null?void 0:h.utm_term)==r.utm_term;return p&&w&&x&&m}return!1}static rr(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o{if(o.selector){var a;kt.rr("applying transform of variant "+n+" for experiment "+t+" ",o);var i=(a=document)==null?void 0:a.querySelectorAll(o.selector);i==null||i.forEach(l=>{var c=l;o.html&&(c.innerHTML=o.html),o.css&&c.setAttribute("style",o.css)})}}):kt.rr("Control variants leave the page unmodified.")}_is_bot(){return as&&this._instance?j2(as,this._instance.config.custom_blocked_useragents):void 0}}var EO=it("[PostHog ExternalIntegrations]"),TO={intercom:"intercom-integration",crispChat:"crisp-chat-integration"};class IO{constructor(t){this._instance=t}it(t,n){var r;(r=ne.__PosthogExtensions__)==null||r.loadExternalDependency==null||r.loadExternalDependency(this._instance,t,o=>{if(o)return EO.error("failed to load script",o);n()})}startIfEnabledOrStop(){var t=this,n=function(i){var l,c,d;!o||(l=ne.__PosthogExtensions__)!=null&&(l=l.integrations)!=null&&l[i]||t.it(TO[i],()=>{var u;(u=ne.__PosthogExtensions__)==null||(u=u.integrations)==null||(u=u[i])==null||u.start(t._instance)}),!o&&(c=ne.__PosthogExtensions__)!=null&&(c=c.integrations)!=null&&c[i]&&((d=ne.__PosthogExtensions__)==null||(d=d.integrations)==null||(d=d[i])==null||d.stop())};for(var[r,o]of Object.entries((a=this._instance.config.integrations)!==null&&a!==void 0?a:{})){var a;n(r)}}}var Vp="[SessionRecording]",hi=it(Vp);class kb{get started(){var t;return!((t=this.vr)==null||!t.isStarted)}get status(){return this.vr?this.vr.status:this.dr&&!this.cr?"disabled":"lazy_loading"}constructor(t){if(this._forceAllowLocalhostNetworkCapture=!1,this.dr=!1,this.pr=void 0,this._instance=t,!this._instance.sessionManager)throw hi.error("started without valid sessionManager"),new Error(Vp+" started without valid sessionManager. This is a bug.");if(this._instance.config.cookieless_mode==="always")throw new Error(Vp+' cannot be used with cookieless_mode="always"')}get cr(){var t,n=!((t=this._instance.get_property(Wh))==null||!t.enabled),r=!this._instance.config.disable_session_recording,o=this._instance.config.disable_session_recording||this._instance.consent.isOptedOut();return S&&n&&r&&!o}startIfEnabledOrStop(t){var n;if(!this.cr||(n=this.vr)==null||!n.isStarted){var r=!L(Object.assign)&&!L(Array.from);this.cr&&r?(this.gr(t),hi.info("starting")):this.stopRecording()}}gr(t){var n,r,o;this.cr&&(ne!=null&&(n=ne.__PosthogExtensions__)!=null&&(n=n.rrweb)!=null&&n.record&&(r=ne.__PosthogExtensions__)!=null&&r.initSessionRecording?this._r(t):(o=ne.__PosthogExtensions__)==null||o.loadExternalDependency==null||o.loadExternalDependency(this._instance,this.mr,a=>{if(a)return hi.error("could not load recorder",a);this._r(t)}))}stopRecording(){var t,n;(t=this.pr)==null||t.call(this),this.pr=void 0,(n=this.vr)==null||n.stop()}yr(){var t;(t=this._instance.persistence)==null||t.unregister(Ck)}br(t){if(this._instance.persistence){var n,r,o=this._instance.persistence,a=()=>{var i=t.sessionRecording===!1?void 0:t.sessionRecording,l=i==null?void 0:i.sampleRate,c=Ie(l)?null:parseFloat(l);Ie(c)&&this.yr();var d=i==null?void 0:i.minimumDurationMilliseconds;o.register({[Wh]:G({enabled:!!i},i,{networkPayloadCapture:G({capturePerformance:t.capturePerformance},i==null?void 0:i.networkPayloadCapture),canvasRecording:{enabled:i==null?void 0:i.recordCanvas,fps:i==null?void 0:i.canvasFps,quality:i==null?void 0:i.canvasQuality},sampleRate:c,minimumDurationMilliseconds:L(d)?null:d,endpoint:i==null?void 0:i.endpoint,triggerMatchType:i==null?void 0:i.triggerMatchType,masking:i==null?void 0:i.masking,urlTriggers:i==null?void 0:i.urlTriggers})})};a(),(n=this.pr)==null||n.call(this),this.pr=(r=this._instance.sessionManager)==null?void 0:r.onSessionId(a)}}onRemoteConfig(t){"sessionRecording"in t?t.sessionRecording!==!1?(this.br(t),this.dr=!0,this.startIfEnabledOrStop()):this.dr=!0:hi.info("skipping remote config with no sessionRecording",t)}log(t,n){var r;n===void 0&&(n="log"),(r=this.vr)!=null&&r.log?this.vr.log(t,n):hi.warn("log called before recorder was ready")}get mr(){var t,n,r=(t=this._instance)==null||(t=t.persistence)==null?void 0:t.get_property(Wh);return(r==null||(n=r.scriptConfig)==null?void 0:n.script)||"lazy-recorder"}_r(t){var n,r;if((n=ne.__PosthogExtensions__)==null||!n.initSessionRecording)throw Error("Called on script loaded before session recording is available");this.vr||(this.vr=(r=ne.__PosthogExtensions__)==null?void 0:r.initSessionRecording(this._instance),this.vr._forceAllowLocalhostNetworkCapture=this._forceAllowLocalhostNetworkCapture),this.vr.start(t)}onRRwebEmit(t){var n;(n=this.vr)==null||n.onRRwebEmit==null||n.onRRwebEmit(t)}overrideLinkedFlag(){var t,n;this.vr||(n=this._instance.persistence)==null||n.register({$replay_override_linked_flag:!0}),(t=this.vr)==null||t.overrideLinkedFlag()}overrideSampling(){var t,n;this.vr||(n=this._instance.persistence)==null||n.register({$replay_override_sampling:!0}),(t=this.vr)==null||t.overrideSampling()}overrideTrigger(t){var n,r;this.vr||(r=this._instance.persistence)==null||r.register({[t==="url"?"$replay_override_url_trigger":"$replay_override_event_trigger"]:!0}),(n=this.vr)==null||n.overrideTrigger(t)}get sdkDebugProperties(){var t;return((t=this.vr)==null?void 0:t.sdkDebugProperties)||{$recording_status:this.status}}tryAddCustomEvent(t,n){var r;return!((r=this.vr)==null||!r.tryAddCustomEvent(t,n))}}var zi={},Gp=()=>{},Oo="posthog",N2=!oO&&(Bt==null?void 0:Bt.indexOf("MSIE"))===-1&&(Bt==null?void 0:Bt.indexOf("Mozilla"))===-1,_b=e=>{var t;return G({api_host:"https://us.i.posthog.com",flags_api_host:null,ui_host:null,token:"",autocapture:!0,cross_subdomain_cookie:Z8(z==null?void 0:z.location),persistence:"localStorage+cookie",persistence_name:"",loaded:Gp,save_campaign_params:!0,custom_campaign_params:[],custom_blocked_useragents:[],save_referrer:!0,capture_pageleave:"if_capture_pageview",defaults:e??"unset",__preview_deferred_init_extensions:!1,debug:qt&&ot(qt==null?void 0:qt.search)&&qt.search.indexOf("__posthog_debug=true")!==-1||!1,cookie_expiration:365,upgrade:!1,disable_session_recording:!1,disable_persistence:!1,disable_web_experiments:!0,disable_surveys:!1,disable_surveys_automatic_display:!1,disable_external_dependency_loading:!1,enable_recording_console_log:void 0,secure_cookie:(S==null||(t=S.location)==null?void 0:t.protocol)==="https:",ip:!1,opt_out_capturing_by_default:!1,opt_out_persistence_by_default:!1,opt_out_useragent_filter:!1,opt_out_capturing_persistence_type:"localStorage",consent_persistence_name:null,opt_out_capturing_cookie_prefix:null,opt_in_site_apps:!1,property_denylist:[],respect_dnt:!1,sanitize_properties:null,request_headers:{},request_batching:!0,properties_string_max_length:65535,mask_all_element_attributes:!1,mask_all_text:!1,mask_personal_data_properties:!1,custom_personal_data_properties:[],advanced_disable_flags:!1,advanced_disable_decide:!1,advanced_disable_feature_flags:!1,advanced_disable_feature_flags_on_first_load:!1,advanced_only_evaluate_survey_feature_flags:!1,advanced_enable_surveys:!1,advanced_disable_toolbar_metrics:!1,feature_flag_request_timeout_ms:3e3,surveys_request_timeout_ms:1e4,on_request_error:n=>{var r="Bad HTTP status: "+n.statusCode+" "+n.text;U.error(r)},get_device_id:n=>n,capture_performance:void 0,name:"posthog",bootstrap:{},disable_compression:!1,session_idle_timeout_seconds:1800,person_profiles:"identified_only",before_send:void 0,request_queue_config:{flush_interval_ms:qp},error_tracking:{},_onCapture:Gp,__preview_eager_load_replay:!1},(n=>({rageclick:!(n&&n>="2025-11-30")||{content_ignorelist:!0},capture_pageview:!(n&&n>="2025-05-24")||"history_change",session_recording:n&&n>="2025-11-30"?{strictMinimumDuration:!0}:{}}))(e))},Cb=e=>{var t={};L(e.process_person)||(t.person_profiles=e.process_person),L(e.xhr_headers)||(t.request_headers=e.xhr_headers),L(e.cookie_name)||(t.persistence_name=e.cookie_name),L(e.disable_cookie)||(t.disable_persistence=e.disable_cookie),L(e.store_google)||(t.save_campaign_params=e.store_google),L(e.verbose)||(t.debug=e.verbose);var n=Je({},t,e);return Ae(e.property_blacklist)&&(L(e.property_denylist)?n.property_denylist=e.property_blacklist:Ae(e.property_denylist)?n.property_denylist=[...e.property_blacklist,...e.property_denylist]:U.error("Invalid value for property_denylist config: "+e.property_denylist)),n};class AO{constructor(){this.__forceAllowLocalhost=!1}get wr(){return this.__forceAllowLocalhost}set wr(t){U.error("WebPerformanceObserver is deprecated and has no impact on network capture. Use `_forceAllowLocalhostNetworkCapture` on `posthog.sessionRecording`"),this.__forceAllowLocalhost=t}}class zu{get decideEndpointWasHit(){var t,n;return(t=(n=this.featureFlags)==null?void 0:n.hasLoadedFlags)!==null&&t!==void 0&&t}get flagsEndpointWasHit(){var t,n;return(t=(n=this.featureFlags)==null?void 0:n.hasLoadedFlags)!==null&&t!==void 0&&t}constructor(){this.webPerformance=new AO,this.Sr=!1,this.version=vn.LIB_VERSION,this.Er=new Uf,this._calculate_event_properties=this.calculateEventProperties.bind(this),this.config=_b(),this.SentryIntegration=E5,this.sentryIntegration=t=>function(n,r){var o=Hk(n,r);return{name:Uk,processEvent:a=>o(a)}}(this,t),this.__request_queue=[],this.__loaded=!1,this.analyticsDefaultEndpoint="/e/",this.$r=!1,this.kr=null,this.Pr=null,this.Tr=null,this.featureFlags=new cO(this),this.toolbar=new I5(this),this.scrollManager=new jO(this),this.pageViewManager=new K5(this),this.surveys=new fO(this),this.experiments=new kt(this),this.exceptions=new aO(this),this.rateLimiter=new gO(this),this.requestRouter=new CO(this),this.consent=new j5(this),this.externalIntegrations=new IO(this),this.people={set:(t,n,r)=>{var o=ot(t)?{[t]:n}:t;this.setPersonProperties(o),r==null||r({})},set_once:(t,n,r)=>{var o=ot(t)?{[t]:n}:t;this.setPersonProperties(void 0,o),r==null||r({})}},this.on("eventCaptured",t=>U.info('send "'+(t==null?void 0:t.event)+'"',t))}init(t,n,r){if(r&&r!==Oo){var o,a=(o=zi[r])!==null&&o!==void 0?o:new zu;return a._init(t,n,r),zi[r]=a,zi[Oo][r]=a,a}return this._init(t,n,r)}_init(t,n,r){var o;if(n===void 0&&(n={}),L(t)||Cp(t))return U.critical("PostHog was initialized without a token. This likely indicates a misconfiguration. Please check the first argument passed to posthog.init()"),this;if(this.__loaded)return console.warn("[PostHog.js]","You have already initialized PostHog! Re-initializing is a no-op"),this;this.__loaded=!0,this.config={},n.debug=this.Rr(n.debug),this.Ir=n,this.Fr=[],n.person_profiles&&(this.Pr=n.person_profiles),this.set_config(Je({},_b(n.defaults),Cb(n),{name:r,token:t})),this.config.on_xhr_error&&U.error("on_xhr_error is deprecated. Use on_request_error instead"),this.compression=n.disable_compression?void 0:Nn.GZipJS;var a=this.Cr();this.persistence=new Zh(this.config,a),this.sessionPersistence=this.config.persistence==="sessionStorage"||this.config.persistence==="memory"?this.persistence:new Zh(G({},this.config,{persistence:"sessionStorage"}),a);var i=G({},this.persistence.props),l=G({},this.sessionPersistence.props);this.register({$initialization_time:new Date().toISOString()}),this.Mr=new yO(g=>this.Or(g),this.config.request_queue_config),this.Ar=new wO(this),this.__request_queue=[];var c=this.config.cookieless_mode==="always"||this.config.cookieless_mode==="on_reject"&&this.consent.isExplicitlyOptedOut();if(c||(this.sessionManager=new jb(this),this.sessionPropsManager=new wb(this,this.sessionManager,this.persistence)),this.config.__preview_deferred_init_extensions?(U.info("Deferring extension initialization to improve startup performance"),setTimeout(()=>{this.Dr(c)},0)):(U.info("Initializing extensions synchronously"),this.Dr(c)),vn.DEBUG=vn.DEBUG||this.config.debug,vn.DEBUG&&U.info("Starting in debug mode",{this:this,config:n,thisC:G({},this.config),p:i,s:l}),((o=n.bootstrap)==null?void 0:o.distinctID)!==void 0){var d,u,h=this.config.get_device_id(ur()),p=(d=n.bootstrap)!=null&&d.isIdentifiedID?h:n.bootstrap.distinctID;this.persistence.set_property(yn,(u=n.bootstrap)!=null&&u.isIdentifiedID?"identified":"anonymous"),this.register({distinct_id:n.bootstrap.distinctID,$device_id:p})}if(this.jr()){var m,w,x=Object.keys(((m=n.bootstrap)==null?void 0:m.featureFlags)||{}).filter(g=>{var y;return!((y=n.bootstrap)==null||(y=y.featureFlags)==null||!y[g])}).reduce((g,y)=>{var j;return g[y]=((j=n.bootstrap)==null||(j=j.featureFlags)==null?void 0:j[y])||!1,g},{}),b=Object.keys(((w=n.bootstrap)==null?void 0:w.featureFlagPayloads)||{}).filter(g=>x[g]).reduce((g,y)=>{var j,N;return(j=n.bootstrap)!=null&&(j=j.featureFlagPayloads)!=null&&j[y]&&(g[y]=(N=n.bootstrap)==null||(N=N.featureFlagPayloads)==null?void 0:N[y]),g},{});this.featureFlags.receivedFeatureFlags({featureFlags:x,featureFlagPayloads:b})}if(c)this.register_once({distinct_id:ri,$device_id:null},"");else if(!this.get_distinct_id()){var v=this.config.get_device_id(ur());this.register_once({distinct_id:v,$device_id:v},""),this.persistence.set_property(yn,"anonymous")}return nt(S,"onpagehide"in self?"pagehide":"unload",this._handle_unload.bind(this),{passive:!1}),this.toolbar.maybeLoadToolbar(),n.segment?S5(this,()=>this.Lr()):this.Lr(),Nr(this.config._onCapture)&&this.config._onCapture!==Gp&&(U.warn("onCapture is deprecated. Please use `before_send` instead"),this.on("eventCaptured",g=>this.config._onCapture(g.event,g))),this.config.ip&&U.warn('The `ip` config option has NO EFFECT AT ALL and has been deprecated. Use a custom transformation or "Discard IP data" project setting instead. See https://posthog.com/tutorials/web-redact-properties#hiding-customer-ip-address for more information.'),this}Dr(t){var n=performance.now();this.historyAutocapture=new C5(this),this.historyAutocapture.startIfEnabled();var r=[];r.push(()=>{new P5(this).startIfEnabledOrStop()}),r.push(()=>{var o;this.siteApps=new _O(this),(o=this.siteApps)==null||o.init()}),t||r.push(()=>{this.sessionRecording=new kb(this),this.sessionRecording.startIfEnabledOrStop()}),this.config.disable_scroll_properties||r.push(()=>{this.scrollManager.startMeasuringScrollPosition()}),r.push(()=>{this.autocapture=new p5(this),this.autocapture.startIfEnabled()}),r.push(()=>{this.surveys.loadIfEnabled()}),r.push(()=>{this.heatmaps=new Q5(this),this.heatmaps.startIfEnabled()}),r.push(()=>{this.webVitalsAutocapture=new G5(this)}),r.push(()=>{this.exceptionObserver=new _5(this),this.exceptionObserver.startIfEnabled()}),r.push(()=>{this.deadClicksAutocapture=new Wk(this,k5),this.deadClicksAutocapture.startIfEnabled()}),r.push(()=>{if(this.Nr){var o=this.Nr;this.Nr=void 0,this.fi(o)}}),this.Ur(r,n)}Ur(t,n){for(;t.length>0;){if(this.config.__preview_deferred_init_extensions&&performance.now()-n>=30&&t.length>0)return void setTimeout(()=>{this.Ur(t,n)},0);var r=t.shift();if(r)try{r()}catch(a){U.error("Error initializing extension:",a)}}var o=Math.round(performance.now()-n);this.register_for_session({$sdk_debug_extensions_init_method:this.config.__preview_deferred_init_extensions?"deferred":"synchronous",$sdk_debug_extensions_init_time_ms:o}),this.config.__preview_deferred_init_extensions&&U.info("PostHog extensions initialized ("+o+"ms)")}fi(t){var n,r,o,a,i,l,c,d;if(!z||!z.body)return U.info("document not ready yet, trying again in 500 milliseconds..."),void setTimeout(()=>{this.fi(t)},500);this.config.__preview_deferred_init_extensions&&(this.Nr=t),this.compression=void 0,t.supportedCompression&&!this.config.disable_compression&&(this.compression=me(t.supportedCompression,Nn.GZipJS)?Nn.GZipJS:me(t.supportedCompression,Nn.Base64)?Nn.Base64:void 0),(n=t.analytics)!=null&&n.endpoint&&(this.analyticsDefaultEndpoint=t.analytics.endpoint),this.set_config({person_profiles:this.Pr?this.Pr:"identified_only"}),(r=this.siteApps)==null||r.onRemoteConfig(t),(o=this.sessionRecording)==null||o.onRemoteConfig(t),(a=this.autocapture)==null||a.onRemoteConfig(t),(i=this.heatmaps)==null||i.onRemoteConfig(t),this.surveys.onRemoteConfig(t),(l=this.webVitalsAutocapture)==null||l.onRemoteConfig(t),(c=this.exceptionObserver)==null||c.onRemoteConfig(t),this.exceptions.onRemoteConfig(t),(d=this.deadClicksAutocapture)==null||d.onRemoteConfig(t)}Lr(){try{this.config.loaded(this)}catch(t){U.critical("`loaded` function failed",t)}this.zr(),this.config.capture_pageview&&setTimeout(()=>{(this.consent.isOptedIn()||this.config.cookieless_mode==="always")&&this.Hr()},1),new vO(this).load(),this.featureFlags.flags()}zr(){var t;this.is_capturing()&&this.config.request_batching&&((t=this.Mr)==null||t.enable())}_dom_loaded(){this.is_capturing()&&_r(this.__request_queue,t=>this.Or(t)),this.__request_queue=[],this.zr()}_handle_unload(){var t,n;this.config.request_batching?(this.Br()&&this.capture("$pageleave"),(t=this.Mr)==null||t.unload(),(n=this.Ar)==null||n.unload()):this.Br()&&this.capture("$pageleave",null,{transport:"sendBeacon"})}ci(t){this.__loaded&&(N2?this.__request_queue.push(t):this.rateLimiter.isServerRateLimited(t.batchKey)||(t.transport=t.transport||this.config.api_transport,t.url=Hd(t.url,{ip:this.config.ip?1:0}),t.headers=G({},this.config.request_headers),t.compression=t.compression==="best-available"?this.compression:t.compression,t.disableXHRCredentials=this.config.__preview_disable_xhr_credentials,this.config.__preview_disable_beacon&&(t.disableTransport=["sendBeacon"]),t.fetchOptions=t.fetchOptions||this.config.fetch_options,(n=>{var r,o,a,i=G({},n);i.timeout=i.timeout||6e4,i.url=Hd(i.url,{_:new Date().getTime().toString(),ver:vn.LIB_VERSION,compression:i.compression});var l=(r=i.transport)!==null&&r!==void 0?r:"fetch",c=Bc.filter(u=>!i.disableTransport||!u.transport||!i.disableTransport.includes(u.transport)),d=(o=(a=Nk(c,u=>u.transport===l))==null?void 0:a.method)!==null&&o!==void 0?o:c[0].method;if(!d)throw new Error("No available transport method");d(i)})(G({},t,{callback:n=>{var r,o;this.rateLimiter.checkForLimiting(n),n.statusCode>=400&&((r=(o=this.config).on_request_error)==null||r.call(o,n)),t.callback==null||t.callback(n)}}))))}Or(t){this.Ar?this.Ar.retriableRequest(t):this.ci(t)}_execute_array(t){var n,r=[],o=[],a=[];_r(t,l=>{l&&(n=l[0],Ae(n)?a.push(l):Nr(l)?l.call(this):Ae(l)&&n==="alias"?r.push(l):Ae(l)&&n.indexOf("capture")!==-1&&Nr(this[n])?a.push(l):o.push(l))});var i=function(l,c){_r(l,function(d){if(Ae(d[0])){var u=c;De(d,function(h){u=u[h[0]].apply(u,h.slice(1))})}else this[d[0]].apply(this,d.slice(1))},c)};i(r,this),i(o,this),i(a,this)}jr(){var t,n;return((t=this.config.bootstrap)==null?void 0:t.featureFlags)&&Object.keys((n=this.config.bootstrap)==null?void 0:n.featureFlags).length>0||!1}push(t){this._execute_array([t])}capture(t,n,r){var o;if(this.__loaded&&this.persistence&&this.sessionPersistence&&this.Mr){if(this.is_capturing())if(!L(t)&&ot(t)){var a=!this.config.opt_out_useragent_filter&&this._is_bot();if(!(a&&!this.config.__preview_capture_bot_pageviews)){var i=r!=null&&r.skip_client_rate_limiting?void 0:this.rateLimiter.clientRateLimitContext();if(i==null||!i.isRateLimited){n!=null&&n.$current_url&&!ot(n==null?void 0:n.$current_url)&&(U.error("Invalid `$current_url` property provided to `posthog.capture`. Input must be a string. Ignoring provided value."),n==null||delete n.$current_url),this.sessionPersistence.update_search_keyword(),this.config.save_campaign_params&&this.sessionPersistence.update_campaign_params(),this.config.save_referrer&&this.sessionPersistence.update_referrer_info(),(this.config.save_campaign_params||this.config.save_referrer)&&this.persistence.set_initial_person_info();var l=new Date,c=(r==null?void 0:r.timestamp)||l,d=ur(),u={uuid:d,event:t,properties:this.calculateEventProperties(t,n||{},c,d)};t==="$pageview"&&this.config.__preview_capture_bot_pageviews&&a&&(u.event="$bot_pageview",u.properties.$browser_type="bot"),i&&(u.properties.$lib_rate_limit_remaining_tokens=i.remainingTokens),r!=null&&r.$set&&(u.$set=r==null?void 0:r.$set);var h,p=this.qr(r==null?void 0:r.$set_once);if(p&&(u.$set_once=p),(u=X8(u,r!=null&&r._noTruncate?null:this.config.properties_string_max_length)).timestamp=c,L(r==null?void 0:r.timestamp)||(u.properties.$event_time_override_provided=!0,u.properties.$event_time_override_system_time=l),t===Wc.DISMISSED||t===Wc.SENT){var m=n==null?void 0:n[vb.SURVEY_ID],w=n==null?void 0:n[vb.SURVEY_ITERATION];h={id:m,current_iteration:w},localStorage.getItem(yb(h))||localStorage.setItem(yb(h),"true"),u.$set=G({},u.$set,{[hO({id:m,current_iteration:w},t===Wc.SENT?"responded":"dismissed")]:!0})}var x=G({},u.properties.$set,u.$set);if(Go(x)||this.setPersonPropertiesForFlags(x),!Ie(this.config.before_send)){var b=this.Wr(u);if(!b)return;u=b}this.Er.emit("eventCaptured",u);var v={method:"POST",url:(o=r==null?void 0:r._url)!==null&&o!==void 0?o:this.requestRouter.endpointFor("api",this.analyticsDefaultEndpoint),data:u,compression:"best-available",batchKey:r==null?void 0:r._batchKey};return!this.config.request_batching||r&&(r==null||!r._batchKey)||r!=null&&r.send_instantly?this.Or(v):this.Mr.enqueue(v),u}U.critical("This capture call is ignored due to client rate limiting.")}}else U.error("No event name provided to posthog.capture")}else U.uninitializedWarning("posthog.capture")}Ai(t){return this.on("eventCaptured",n=>t(n.event,n))}calculateEventProperties(t,n,r,o,a){if(r=r||new Date,!this.persistence||!this.sessionPersistence)return n;var i=a?void 0:this.persistence.remove_event_timer(t),l=G({},n);if(l.token=this.config.token,l.$config_defaults=this.config.defaults,(this.config.cookieless_mode=="always"||this.config.cookieless_mode=="on_reject"&&this.consent.isExplicitlyOptedOut())&&(l.$cookieless_mode=!0),t==="$snapshot"){var c=G({},this.persistence.properties(),this.sessionPersistence.properties());return l.distinct_id=c.distinct_id,(!ot(l.distinct_id)&&!un(l.distinct_id)||Cp(l.distinct_id))&&U.error("Invalid distinct_id for replay event. This indicates a bug in your implementation"),l}var d,u=V5(this.config.mask_personal_data_properties,this.config.custom_personal_data_properties);if(this.sessionManager){var{sessionId:h,windowId:p}=this.sessionManager.checkAndGetSessionAndWindowId(a,r.getTime());l.$session_id=h,l.$window_id=p}this.sessionPropsManager&&Je(l,this.sessionPropsManager.getSessionProps());try{var m;this.sessionRecording&&Je(l,this.sessionRecording.sdkDebugProperties),l.$sdk_debug_retry_queue_size=(m=this.Ar)==null?void 0:m.length}catch(v){l.$sdk_debug_error_capturing_properties=String(v)}if(this.requestRouter.region===Ni.CUSTOM&&(l.$lib_custom_api_host=this.config.api_host),d=t!=="$pageview"||a?t!=="$pageleave"||a?this.pageViewManager.doEvent():this.pageViewManager.doPageLeave(r):this.pageViewManager.doPageView(r,o),l=Je(l,d),t==="$pageview"&&z&&(l.title=z.title),!L(i)){var w=r.getTime()-i;l.$duration=parseFloat((w/1e3).toFixed(3))}Bt&&this.config.opt_out_useragent_filter&&(l.$browser_type=this._is_bot()?"bot":"browser"),(l=Je({},u,this.persistence.properties(),this.sessionPersistence.properties(),l)).$is_identified=this._isIdentified(),Ae(this.config.property_denylist)?De(this.config.property_denylist,function(v){delete l[v]}):U.error("Invalid value for property_denylist config: "+this.config.property_denylist+" or property_blacklist config: "+this.config.property_blacklist);var x=this.config.sanitize_properties;x&&(U.error("sanitize_properties is deprecated. Use before_send instead"),l=x(l,t));var b=this.Gr();return l.$process_person_profile=b,b&&!a&&this.Vr("_calculate_event_properties"),l}qr(t){var n;if(!this.persistence||!this.Gr()||this.Sr)return t;var r=this.persistence.get_initial_props(),o=(n=this.sessionPropsManager)==null?void 0:n.getSetOnceProps(),a=Je({},r,o||{},t||{}),i=this.config.sanitize_properties;return i&&(U.error("sanitize_properties is deprecated. Use before_send instead"),a=i(a,"$set_once")),this.Sr=!0,Go(a)?void 0:a}register(t,n){var r;(r=this.persistence)==null||r.register(t,n)}register_once(t,n,r){var o;(o=this.persistence)==null||o.register_once(t,n,r)}register_for_session(t){var n;(n=this.sessionPersistence)==null||n.register(t)}unregister(t){var n;(n=this.persistence)==null||n.unregister(t)}unregister_for_session(t){var n;(n=this.sessionPersistence)==null||n.unregister(t)}Jr(t,n){this.register({[t]:n})}getFeatureFlag(t,n){return this.featureFlags.getFeatureFlag(t,n)}getFeatureFlagPayload(t){var n=this.featureFlags.getFeatureFlagPayload(t);try{return JSON.parse(n)}catch{return n}}isFeatureEnabled(t,n){return this.featureFlags.isFeatureEnabled(t,n)}reloadFeatureFlags(){this.featureFlags.reloadFeatureFlags()}updateEarlyAccessFeatureEnrollment(t,n,r){this.featureFlags.updateEarlyAccessFeatureEnrollment(t,n,r)}getEarlyAccessFeatures(t,n,r){return n===void 0&&(n=!1),this.featureFlags.getEarlyAccessFeatures(t,n,r)}on(t,n){return this.Er.on(t,n)}onFeatureFlags(t){return this.featureFlags.onFeatureFlags(t)}onSurveysLoaded(t){return this.surveys.onSurveysLoaded(t)}onSessionId(t){var n,r;return(n=(r=this.sessionManager)==null?void 0:r.onSessionId(t))!==null&&n!==void 0?n:()=>{}}getSurveys(t,n){n===void 0&&(n=!1),this.surveys.getSurveys(t,n)}getActiveMatchingSurveys(t,n){n===void 0&&(n=!1),this.surveys.getActiveMatchingSurveys(t,n)}renderSurvey(t,n){this.surveys.renderSurvey(t,n)}displaySurvey(t,n){n===void 0&&(n=pO),this.surveys.displaySurvey(t,n)}cancelPendingSurvey(t){this.surveys.cancelPendingSurvey(t)}canRenderSurvey(t){return this.surveys.canRenderSurvey(t)}canRenderSurveyAsync(t,n){return n===void 0&&(n=!1),this.surveys.canRenderSurveyAsync(t,n)}identify(t,n,r){if(!this.__loaded||!this.persistence)return U.uninitializedWarning("posthog.identify");if(un(t)&&(t=t.toString(),U.warn("The first argument to posthog.identify was a number, but it should be a string. It has been converted to a string.")),t)if(["distinct_id","distinctid"].includes(t.toLowerCase()))U.critical('The string "'+t+'" was set in posthog.identify which indicates an error. This ID should be unique to the user and not a hardcoded string.');else if(t!==ri){if(this.Vr("posthog.identify")){var o=this.get_distinct_id();if(this.register({$user_id:t}),!this.get_property("$device_id")){var a=o;this.register_once({$had_persisted_distinct_id:!0,$device_id:a},"")}t!==o&&t!==this.get_property(gi)&&(this.unregister(gi),this.register({distinct_id:t}));var i=(this.persistence.get_property(yn)||"anonymous")==="anonymous";t!==o&&i?(this.persistence.set_property(yn,"identified"),this.setPersonPropertiesForFlags(G({},r||{},n||{}),!1),this.capture("$identify",{distinct_id:t,$anon_distinct_id:o},{$set:n||{},$set_once:r||{}}),this.Tr=mb(t,n,r),this.featureFlags.setAnonymousDistinctId(o)):(n||r)&&this.setPersonProperties(n,r),t!==o&&(this.reloadFeatureFlags(),this.unregister(Dd))}}else U.critical('The string "'+ri+'" was set in posthog.identify which indicates an error. This ID is only used as a sentinel value.');else U.error("Unique user id has not been set in posthog.identify")}setPersonProperties(t,n){if((t||n)&&this.Vr("posthog.setPersonProperties")){var r=mb(this.get_distinct_id(),t,n);this.Tr!==r?(this.setPersonPropertiesForFlags(G({},n||{},t||{})),this.capture("$set",{$set:t||{},$set_once:n||{}}),this.Tr=r):U.info("A duplicate setPersonProperties call was made with the same properties. It has been ignored.")}}group(t,n,r){if(t&&n){if(this.Vr("posthog.group")){var o=this.getGroups();o[t]!==n&&this.resetGroupPropertiesForFlags(t),this.register({$groups:G({},o,{[t]:n})}),r&&(this.capture("$groupidentify",{$group_type:t,$group_key:n,$group_set:r}),this.setGroupPropertiesForFlags({[t]:r})),o[t]===n||r||this.reloadFeatureFlags()}}else U.error("posthog.group requires a group type and group key")}resetGroups(){this.register({$groups:{}}),this.resetGroupPropertiesForFlags(),this.reloadFeatureFlags()}setPersonPropertiesForFlags(t,n){n===void 0&&(n=!0),this.featureFlags.setPersonPropertiesForFlags(t,n)}resetPersonPropertiesForFlags(){this.featureFlags.resetPersonPropertiesForFlags()}setGroupPropertiesForFlags(t,n){n===void 0&&(n=!0),this.Vr("posthog.setGroupPropertiesForFlags")&&this.featureFlags.setGroupPropertiesForFlags(t,n)}resetGroupPropertiesForFlags(t){this.featureFlags.resetGroupPropertiesForFlags(t)}reset(t){var n,r,o,a;if(U.info("reset"),!this.__loaded)return U.uninitializedWarning("posthog.reset");var i=this.get_property("$device_id");if(this.consent.reset(),(n=this.persistence)==null||n.clear(),(r=this.sessionPersistence)==null||r.clear(),this.surveys.reset(),this.featureFlags.reset(),(o=this.persistence)==null||o.set_property(yn,"anonymous"),(a=this.sessionManager)==null||a.resetSessionId(),this.Tr=null,this.config.cookieless_mode==="always")this.register_once({distinct_id:ri,$device_id:null},"");else{var l=this.config.get_device_id(ur());this.register_once({distinct_id:l,$device_id:t?l:i},"")}this.register({$last_posthog_reset:new Date().toISOString()},1)}get_distinct_id(){return this.get_property("distinct_id")}getGroups(){return this.get_property("$groups")||{}}get_session_id(){var t,n;return(t=(n=this.sessionManager)==null?void 0:n.checkAndGetSessionAndWindowId(!0).sessionId)!==null&&t!==void 0?t:""}get_session_replay_url(t){if(!this.sessionManager)return"";var{sessionId:n,sessionStartTimestamp:r}=this.sessionManager.checkAndGetSessionAndWindowId(!0),o=this.requestRouter.endpointFor("ui","/project/"+this.config.token+"/replay/"+n);if(t!=null&&t.withTimestamp&&r){var a,i=(a=t.timestampLookBack)!==null&&a!==void 0?a:10;if(!r)return o;o+="?t="+Math.max(Math.floor((new Date().getTime()-r)/1e3)-i,0)}return o}alias(t,n){return t===this.get_property(kk)?(U.critical("Attempting to create alias for existing People user - aborting."),-2):this.Vr("posthog.alias")?(L(n)&&(n=this.get_distinct_id()),t!==n?(this.Jr(gi,t),this.capture("$create_alias",{alias:t,distinct_id:n})):(U.warn("alias matches current distinct_id - skipping api call."),this.identify(t),-1)):void 0}set_config(t){var n=G({},this.config);if(ct(t)){var r,o,a,i,l;Je(this.config,Cb(t));var c=this.Cr();(r=this.persistence)==null||r.update_config(this.config,n,c),this.sessionPersistence=this.config.persistence==="sessionStorage"||this.config.persistence==="memory"?this.persistence:new Zh(G({},this.config,{persistence:"sessionStorage"}),c);var d=this.Rr(this.config.debug);kr(d)&&(this.config.debug=d),kr(this.config.debug)&&(this.config.debug?(vn.DEBUG=!0,Ge.H()&&Ge.G("ph_debug","true"),U.info("set_config",{config:t,oldConfig:n,newConfig:G({},this.config)})):(vn.DEBUG=!1,Ge.H()&&Ge.V("ph_debug"))),(o=this.sessionRecording)==null||o.startIfEnabledOrStop(),(a=this.autocapture)==null||a.startIfEnabled(),(i=this.heatmaps)==null||i.startIfEnabled(),this.surveys.loadIfEnabled(),this.Kr(),(l=this.externalIntegrations)==null||l.startIfEnabledOrStop()}}startSessionRecording(t){var n=t===!0,r={sampling:n||!(t==null||!t.sampling),linked_flag:n||!(t==null||!t.linked_flag),url_trigger:n||!(t==null||!t.url_trigger),event_trigger:n||!(t==null||!t.event_trigger)};if(Object.values(r).some(Boolean)){var o,a,i,l,c;(o=this.sessionManager)==null||o.checkAndGetSessionAndWindowId(),r.sampling&&((a=this.sessionRecording)==null||a.overrideSampling()),r.linked_flag&&((i=this.sessionRecording)==null||i.overrideLinkedFlag()),r.url_trigger&&((l=this.sessionRecording)==null||l.overrideTrigger("url")),r.event_trigger&&((c=this.sessionRecording)==null||c.overrideTrigger("event"))}this.set_config({disable_session_recording:!1})}stopSessionRecording(){this.set_config({disable_session_recording:!0})}sessionRecordingStarted(){var t;return!((t=this.sessionRecording)==null||!t.started)}captureException(t,n){var r=new Error("PostHog syntheticException"),o=this.exceptions.buildProperties(t,{handled:!0,syntheticException:r});return this.exceptions.sendExceptionEvent(G({},o,n))}loadToolbar(t){return this.toolbar.loadToolbar(t)}get_property(t){var n;return(n=this.persistence)==null?void 0:n.props[t]}getSessionProperty(t){var n;return(n=this.sessionPersistence)==null?void 0:n.props[t]}toString(){var t,n=(t=this.config.name)!==null&&t!==void 0?t:Oo;return n!==Oo&&(n=Oo+"."+n),n}_isIdentified(){var t,n;return((t=this.persistence)==null?void 0:t.get_property(yn))==="identified"||((n=this.sessionPersistence)==null?void 0:n.get_property(yn))==="identified"}Gr(){var t,n;return!(this.config.person_profiles==="never"||this.config.person_profiles==="identified_only"&&!this._isIdentified()&&Go(this.getGroups())&&((t=this.persistence)==null||(t=t.props)==null||!t[gi])&&((n=this.persistence)==null||(n=n.props)==null||!n[Md]))}Br(){return this.config.capture_pageleave===!0||this.config.capture_pageleave==="if_capture_pageview"&&(this.config.capture_pageview===!0||this.config.capture_pageview==="history_change")}createPersonProfile(){this.Gr()||this.Vr("posthog.createPersonProfile")&&this.setPersonProperties({},{})}Vr(t){return this.config.person_profiles==="never"?(U.error(t+' was called, but process_person is set to "never". This call will be ignored.'),!1):(this.Jr(Md,!0),!0)}Cr(){if(this.config.cookieless_mode==="always")return!0;var t=this.consent.isOptedOut(),n=this.config.opt_out_persistence_by_default||this.config.cookieless_mode==="on_reject";return this.config.disable_persistence||t&&!!n}Kr(){var t,n,r,o,a=this.Cr();return((t=this.persistence)==null?void 0:t.wi)!==a&&((r=this.persistence)==null||r.set_disabled(a)),((n=this.sessionPersistence)==null?void 0:n.wi)!==a&&((o=this.sessionPersistence)==null||o.set_disabled(a)),a}opt_in_capturing(t){var n;if(this.config.cookieless_mode!=="always"){var r,o;this.config.cookieless_mode==="on_reject"&&this.consent.isExplicitlyOptedOut()&&(this.reset(!0),(r=this.sessionManager)==null||r.destroy(),this.sessionManager=new jb(this),this.persistence&&(this.sessionPropsManager=new wb(this,this.sessionManager,this.persistence)),this.sessionRecording=new kb(this),this.sessionRecording.startIfEnabledOrStop()),this.consent.optInOut(!0),this.Kr(),this.zr(),(n=this.sessionRecording)==null||n.startIfEnabledOrStop(),this.config.cookieless_mode=="on_reject"&&this.surveys.loadIfEnabled(),(L(t==null?void 0:t.captureEventName)||t!=null&&t.captureEventName)&&this.capture((o=t==null?void 0:t.captureEventName)!==null&&o!==void 0?o:"$opt_in",t==null?void 0:t.captureProperties,{send_instantly:!0}),this.config.capture_pageview&&this.Hr()}else U.warn('Consent opt in/out is not valid with cookieless_mode="always" and will be ignored')}opt_out_capturing(){var t,n;this.config.cookieless_mode!=="always"?(this.config.cookieless_mode==="on_reject"&&this.consent.isOptedIn()&&this.reset(!0),this.consent.optInOut(!1),this.Kr(),this.config.cookieless_mode==="on_reject"&&(this.register({distinct_id:ri,$device_id:null}),(t=this.sessionManager)==null||t.destroy(),this.sessionManager=void 0,this.sessionPropsManager=void 0,(n=this.sessionRecording)==null||n.stopRecording(),this.sessionRecording=void 0,this.Hr())):U.warn('Consent opt in/out is not valid with cookieless_mode="always" and will be ignored')}has_opted_in_capturing(){return this.consent.isOptedIn()}has_opted_out_capturing(){return this.consent.isOptedOut()}get_explicit_consent_status(){var t=this.consent.consent;return t===bn.GRANTED?"granted":t===bn.DENIED?"denied":"pending"}is_capturing(){return this.config.cookieless_mode==="always"||(this.config.cookieless_mode==="on_reject"?this.consent.isExplicitlyOptedOut()||this.consent.isOptedIn():!this.has_opted_out_capturing())}clear_opt_in_out_capturing(){this.consent.reset(),this.Kr()}_is_bot(){return as?j2(as,this.config.custom_blocked_useragents):void 0}Hr(){z&&(z.visibilityState==="visible"?this.$r||(this.$r=!0,this.capture("$pageview",{title:z.title},{send_instantly:!0}),this.kr&&(z.removeEventListener("visibilitychange",this.kr),this.kr=null)):this.kr||(this.kr=this.Hr.bind(this),nt(z,"visibilitychange",this.kr)))}debug(t){t===!1?(S==null||S.console.log("You've disabled debug mode."),this.set_config({debug:!1})):(S==null||S.console.log("You're now in debug mode. All calls to PostHog will be logged in your console.\nYou can disable this with `posthog.debug(false)`."),this.set_config({debug:!0}))}O(){var t,n,r,o,a,i,l,c=this.Ir||{};return"advanced_disable_flags"in c?!!c.advanced_disable_flags:this.config.advanced_disable_flags!==!1?!!this.config.advanced_disable_flags:this.config.advanced_disable_decide===!0?(U.warn("Config field 'advanced_disable_decide' is deprecated. Please use 'advanced_disable_flags' instead. The old field will be removed in a future major version."),!0):(r="advanced_disable_decide",o=!1,a=U,i=(n="advanced_disable_flags")in(t=c)&&!L(t[n]),l=r in t&&!L(t[r]),i?t[n]:l?(a&&a.warn("Config field '"+r+"' is deprecated. Please use '"+n+"' instead. The old field will be removed in a future major version."),t[r]):o)}Wr(t){if(Ie(this.config.before_send))return t;var n=Ae(this.config.before_send)?this.config.before_send:[this.config.before_send],r=t;for(var o of n){if(r=o(r),Ie(r)){var a="Event '"+t.event+"' was rejected in beforeSend function";return k8(t.event)?U.warn(a+". This can cause unexpected behavior."):U.info(a),null}r.properties&&!Go(r.properties)||U.warn("Event '"+t.event+"' has no properties after beforeSend function, this is likely an error.")}return r}getPageViewId(){var t;return(t=this.pageViewManager.Vt)==null?void 0:t.pageViewId}captureTraceFeedback(t,n){this.capture("$ai_feedback",{$ai_trace_id:String(t),$ai_feedback_text:n})}captureTraceMetric(t,n,r){this.capture("$ai_metric",{$ai_trace_id:String(t),$ai_metric_name:n,$ai_metric_value:String(r)})}Rr(t){var n=kr(t)&&!t,r=Ge.H()&&Ge.q("ph_debug")==="true";return!n&&(!!r||t)}}(function(e,t){for(var n=0;n{if(typeof window>"u"){a(!1);return}(async()=>{try{const d=await fetch("https://www.cloudflare.com/cdn-cgi/trace",{signal:AbortSignal.timeout(5e3)});if(!d.ok)throw new Error("API request failed");const h=(await d.text()).split(` `),p={};h.forEach(w=>{const[x,b]=w.split("=");x&&b&&(p[x.trim()]=b.trim())});const m=p.loc;if(m){r(m);const w=Eb[m]||Bi;t(w)}}catch{try{const h=(navigator.language||navigator.userLanguage||"").split("-"),p=h.length>1?h[1].toUpperCase():null;if(p){r(p);const m=Eb[p]||Bi;t(m)}}catch{l("Could not detect location")}}finally{a(!1)}})()},[]),{currency:e,country:n,isLoading:o,error:i}}const Tb="preferred_currency",C2=f.createContext(void 0);function zO({children:e}){const{currency:t,country:n,isLoading:r}=FO(),[o,a]=f.useState(Bi),[i,l]=f.useState(!1),[c,d]=f.useState(!1),[u,h]=f.useState(!1);f.useEffect(()=>{if(c)return;const x=localStorage.getItem(Tb);x&&_2.includes(x)?(a(x),l(!1),d(!0)):!r&&t!==Bi?(a(t),l(!0),d(!0)):r||d(!0)},[t,r,c]),f.useEffect(()=>{if(c)return;if(!r){h(!1);return}const x=window.setTimeout(()=>{h(!0)},2e3);return()=>window.clearTimeout(x)},[r,c]);const p=x=>{a(x),l(!1),localStorage.setItem(Tb,x)},m=x=>{const b=LO[x][o];return`${$O[o]}${b}`},w=r&&!c&&!u;return s.jsx(C2.Provider,{value:{currency:o,setCurrency:p,isAutoDetected:i,country:n,formatPrice:m,isLoading:w},children:e})}function S2(){const e=f.useContext(C2);if(e===void 0)throw new Error("useCurrency must be used within a CurrencyProvider");return e}function BO({children:e,fallback:t=null}){const[n,r]=f.useState(!1);return f.useEffect(()=>{r(!0)},[]),n?s.jsx(s.Fragment,{children:e}):s.jsx(s.Fragment,{children:t})}function Ib(e,[t,n]){return Math.min(n,Math.max(t,e))}var WO=f.createContext(void 0);function UO(e){const t=f.useContext(WO);return e||t||"ltr"}function HO(e){const t=f.useRef({value:e,previous:e});return f.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var qO=[" ","Enter","ArrowUp","ArrowDown"],VO=[" ","Enter"],go="Select",[Bu,Wu,GO]=oN(go),[Ua,u$]=Ra(go,[GO,Cu]),Uu=Cu(),[YO,Rr]=Ua(go),[QO,KO]=Ua(go),E2=e=>{const{__scopeSelect:t,children:n,open:r,defaultOpen:o,onOpenChange:a,value:i,defaultValue:l,onValueChange:c,dir:d,name:u,autoComplete:h,disabled:p,required:m,form:w}=e,x=Uu(t),[b,v]=f.useState(null),[g,y]=f.useState(null),[j,N]=f.useState(!1),_=UO(d),[k,C]=kd({prop:r,defaultProp:o??!1,onChange:a,caller:go}),[P,A]=kd({prop:i,defaultProp:l,onChange:c,caller:go}),H=f.useRef(null),M=b?w||!!b.closest("form"):!0,[K,O]=f.useState(new Set),X=Array.from(K).map(q=>q.props.value).join(";");return s.jsx(HI,{...x,children:s.jsxs(YO,{required:m,scope:t,trigger:b,onTriggerChange:v,valueNode:g,onValueNodeChange:y,valueNodeHasChildren:j,onValueNodeHasChildrenChange:N,contentId:na(),value:P,onValueChange:A,open:k,onOpenChange:C,dir:_,triggerPointerDownPosRef:H,disabled:p,children:[s.jsx(Bu.Provider,{scope:t,children:s.jsx(QO,{scope:e.__scopeSelect,onNativeOptionAdd:f.useCallback(q=>{O(Y=>new Set(Y).add(q))},[]),onNativeOptionRemove:f.useCallback(q=>{O(Y=>{const E=new Set(Y);return E.delete(q),E})},[]),children:n})}),M?s.jsxs(J2,{"aria-hidden":!0,required:m,tabIndex:-1,name:u,autoComplete:h,value:P,onChange:q=>A(q.target.value),disabled:p,form:w,children:[P===void 0?s.jsx("option",{value:""}):null,Array.from(K)]},X):null]})})};E2.displayName=go;var T2="SelectTrigger",I2=f.forwardRef((e,t)=>{const{__scopeSelect:n,disabled:r=!1,...o}=e,a=Uu(n),i=Rr(T2,n),l=i.disabled||r,c=We(t,i.onTriggerChange),d=Wu(n),u=f.useRef("touch"),[h,p,m]=e_(x=>{const b=d().filter(y=>!y.disabled),v=b.find(y=>y.value===i.value),g=t_(b,x,v);g!==void 0&&i.onValueChange(g.value)}),w=x=>{l||(i.onOpenChange(!0),m()),x&&(i.triggerPointerDownPosRef.current={x:Math.round(x.pageX),y:Math.round(x.pageY)})};return s.jsx(u1,{asChild:!0,...a,children:s.jsx(be.button,{type:"button",role:"combobox","aria-controls":i.contentId,"aria-expanded":i.open,"aria-required":i.required,"aria-autocomplete":"none",dir:i.dir,"data-state":i.open?"open":"closed",disabled:l,"data-disabled":l?"":void 0,"data-placeholder":Z2(i.value)?"":void 0,...o,ref:c,onClick:le(o.onClick,x=>{x.currentTarget.focus(),u.current!=="mouse"&&w(x)}),onPointerDown:le(o.onPointerDown,x=>{u.current=x.pointerType;const b=x.target;b.hasPointerCapture(x.pointerId)&&b.releasePointerCapture(x.pointerId),x.button===0&&x.ctrlKey===!1&&x.pointerType==="mouse"&&(w(x),x.preventDefault())}),onKeyDown:le(o.onKeyDown,x=>{const b=h.current!=="";!(x.ctrlKey||x.altKey||x.metaKey)&&x.key.length===1&&p(x.key),!(b&&x.key===" ")&&qO.includes(x.key)&&(w(),x.preventDefault())})})})});I2.displayName=T2;var A2="SelectValue",P2=f.forwardRef((e,t)=>{const{__scopeSelect:n,className:r,style:o,children:a,placeholder:i="",...l}=e,c=Rr(A2,n),{onValueNodeHasChildrenChange:d}=c,u=a!==void 0,h=We(t,c.onValueNodeChange);return vt(()=>{d(u)},[d,u]),s.jsx(be.span,{...l,ref:h,style:{pointerEvents:"none"},children:Z2(c.value)?s.jsx(s.Fragment,{children:i}):a})});P2.displayName=A2;var XO="SelectIcon",O2=f.forwardRef((e,t)=>{const{__scopeSelect:n,children:r,...o}=e;return s.jsx(be.span,{"aria-hidden":!0,...o,ref:t,children:r||"▼"})});O2.displayName=XO;var JO="SelectPortal",D2=e=>s.jsx(bu,{asChild:!0,...e});D2.displayName=JO;var vo="SelectContent",R2=f.forwardRef((e,t)=>{const n=Rr(vo,e.__scopeSelect),[r,o]=f.useState();if(vt(()=>{o(new DocumentFragment)},[]),!n.open){const a=r;return a?Oa.createPortal(s.jsx(M2,{scope:e.__scopeSelect,children:s.jsx(Bu.Slot,{scope:e.__scopeSelect,children:s.jsx("div",{children:e.children})})}),a):null}return s.jsx($2,{...e,ref:t})});R2.displayName=vo;var Is=10,[M2,Mr]=Ua(vo),ZO="SelectContentImpl",eD=ka("SelectContent.RemoveScroll"),$2=f.forwardRef((e,t)=>{const{__scopeSelect:n,position:r="item-aligned",onCloseAutoFocus:o,onEscapeKeyDown:a,onPointerDownOutside:i,side:l,sideOffset:c,align:d,alignOffset:u,arrowPadding:h,collisionBoundary:p,collisionPadding:m,sticky:w,hideWhenDetached:x,avoidCollisions:b,...v}=e,g=Rr(vo,n),[y,j]=f.useState(null),[N,_]=f.useState(null),k=We(t,F=>j(F)),[C,P]=f.useState(null),[A,H]=f.useState(null),M=Wu(n),[K,O]=f.useState(!1),X=f.useRef(!1);f.useEffect(()=>{if(y)return F1(y)},[y]),I1();const q=f.useCallback(F=>{const[ke,...Ue]=M().map(fe=>fe.ref.current),[ce]=Ue.slice(-1),xe=document.activeElement;for(const fe of F)if(fe===xe||(fe==null||fe.scrollIntoView({block:"nearest"}),fe===ke&&N&&(N.scrollTop=0),fe===ce&&N&&(N.scrollTop=N.scrollHeight),fe==null||fe.focus(),document.activeElement!==xe))return},[M,N]),Y=f.useCallback(()=>q([C,y]),[q,C,y]);f.useEffect(()=>{K&&Y()},[K,Y]);const{onOpenChange:E,triggerPointerDownPosRef:T}=g;f.useEffect(()=>{if(y){let F={x:0,y:0};const ke=ce=>{var xe,fe;F={x:Math.abs(Math.round(ce.pageX)-(((xe=T.current)==null?void 0:xe.x)??0)),y:Math.abs(Math.round(ce.pageY)-(((fe=T.current)==null?void 0:fe.y)??0))}},Ue=ce=>{F.x<=10&&F.y<=10?ce.preventDefault():y.contains(ce.target)||E(!1),document.removeEventListener("pointermove",ke),T.current=null};return T.current!==null&&(document.addEventListener("pointermove",ke),document.addEventListener("pointerup",Ue,{capture:!0,once:!0})),()=>{document.removeEventListener("pointermove",ke),document.removeEventListener("pointerup",Ue,{capture:!0})}}},[y,E,T]),f.useEffect(()=>{const F=()=>E(!1);return window.addEventListener("blur",F),window.addEventListener("resize",F),()=>{window.removeEventListener("blur",F),window.removeEventListener("resize",F)}},[E]);const[$,Z]=e_(F=>{const ke=M().filter(xe=>!xe.disabled),Ue=ke.find(xe=>xe.ref.current===document.activeElement),ce=t_(ke,F,Ue);ce&&setTimeout(()=>ce.ref.current.focus())}),W=f.useCallback((F,ke,Ue)=>{const ce=!X.current&&!Ue;(g.value!==void 0&&g.value===ke||ce)&&(P(F),ce&&(X.current=!0))},[g.value]),ee=f.useCallback(()=>y==null?void 0:y.focus(),[y]),J=f.useCallback((F,ke,Ue)=>{const ce=!X.current&&!Ue;(g.value!==void 0&&g.value===ke||ce)&&H(F)},[g.value]),we=r==="popper"?Qp:L2,Ne=we===Qp?{side:l,sideOffset:c,align:d,alignOffset:u,arrowPadding:h,collisionBoundary:p,collisionPadding:m,sticky:w,hideWhenDetached:x,avoidCollisions:b}:{};return s.jsx(M2,{scope:n,content:y,viewport:N,onViewportChange:_,itemRefCallback:W,selectedItem:C,onItemLeave:ee,itemTextRefCallback:J,focusSelectedItem:Y,selectedItemText:A,position:r,isPositioned:K,searchRef:$,children:s.jsx(Ef,{as:eD,allowPinchZoom:!0,children:s.jsx(Sf,{asChild:!0,trapped:g.open,onMountAutoFocus:F=>{F.preventDefault()},onUnmountAutoFocus:le(o,F=>{var ke;(ke=g.trigger)==null||ke.focus({preventScroll:!0}),F.preventDefault()}),children:s.jsx(Tl,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:a,onPointerDownOutside:i,onFocusOutside:F=>F.preventDefault(),onDismiss:()=>g.onOpenChange(!1),children:s.jsx(we,{role:"listbox",id:g.contentId,"data-state":g.open?"open":"closed",dir:g.dir,onContextMenu:F=>F.preventDefault(),...v,...Ne,onPlaced:()=>O(!0),ref:k,style:{display:"flex",flexDirection:"column",outline:"none",...v.style},onKeyDown:le(v.onKeyDown,F=>{const ke=F.ctrlKey||F.altKey||F.metaKey;if(F.key==="Tab"&&F.preventDefault(),!ke&&F.key.length===1&&Z(F.key),["ArrowUp","ArrowDown","Home","End"].includes(F.key)){let ce=M().filter(xe=>!xe.disabled).map(xe=>xe.ref.current);if(["ArrowUp","End"].includes(F.key)&&(ce=ce.slice().reverse()),["ArrowUp","ArrowDown"].includes(F.key)){const xe=F.target,fe=ce.indexOf(xe);ce=ce.slice(fe+1)}setTimeout(()=>q(ce)),F.preventDefault()}})})})})})})});$2.displayName=ZO;var tD="SelectItemAlignedPosition",L2=f.forwardRef((e,t)=>{const{__scopeSelect:n,onPlaced:r,...o}=e,a=Rr(vo,n),i=Mr(vo,n),[l,c]=f.useState(null),[d,u]=f.useState(null),h=We(t,k=>u(k)),p=Wu(n),m=f.useRef(!1),w=f.useRef(!0),{viewport:x,selectedItem:b,selectedItemText:v,focusSelectedItem:g}=i,y=f.useCallback(()=>{if(a.trigger&&a.valueNode&&l&&d&&x&&b&&v){const k=a.trigger.getBoundingClientRect(),C=d.getBoundingClientRect(),P=a.valueNode.getBoundingClientRect(),A=v.getBoundingClientRect();if(a.dir!=="rtl"){const xe=A.left-C.left,fe=P.left-xe,tt=k.left-fe,Et=k.width+tt,qs=Math.max(Et,C.width),js=window.innerWidth-Is,Ns=Ib(fe,[Is,Math.max(Is,js-qs)]);l.style.minWidth=Et+"px",l.style.left=Ns+"px"}else{const xe=C.right-A.right,fe=window.innerWidth-P.right-xe,tt=window.innerWidth-k.right-fe,Et=k.width+tt,qs=Math.max(Et,C.width),js=window.innerWidth-Is,Ns=Ib(fe,[Is,Math.max(Is,js-qs)]);l.style.minWidth=Et+"px",l.style.right=Ns+"px"}const H=p(),M=window.innerHeight-Is*2,K=x.scrollHeight,O=window.getComputedStyle(d),X=parseInt(O.borderTopWidth,10),q=parseInt(O.paddingTop,10),Y=parseInt(O.borderBottomWidth,10),E=parseInt(O.paddingBottom,10),T=X+q+K+E+Y,$=Math.min(b.offsetHeight*5,T),Z=window.getComputedStyle(x),W=parseInt(Z.paddingTop,10),ee=parseInt(Z.paddingBottom,10),J=k.top+k.height/2-Is,we=M-J,Ne=b.offsetHeight/2,F=b.offsetTop+Ne,ke=X+q+F,Ue=T-ke;if(ke<=J){const xe=H.length>0&&b===H[H.length-1].ref.current;l.style.bottom="0px";const fe=d.clientHeight-x.offsetTop-x.offsetHeight,tt=Math.max(we,Ne+(xe?ee:0)+fe+Y),Et=ke+tt;l.style.height=Et+"px"}else{const xe=H.length>0&&b===H[0].ref.current;l.style.top="0px";const tt=Math.max(J,X+x.offsetTop+(xe?W:0)+Ne)+Ue;l.style.height=tt+"px",x.scrollTop=ke-J+x.offsetTop}l.style.margin=`${Is}px 0`,l.style.minHeight=$+"px",l.style.maxHeight=M+"px",r==null||r(),requestAnimationFrame(()=>m.current=!0)}},[p,a.trigger,a.valueNode,l,d,x,b,v,a.dir,r]);vt(()=>y(),[y]);const[j,N]=f.useState();vt(()=>{d&&N(window.getComputedStyle(d).zIndex)},[d]);const _=f.useCallback(k=>{k&&w.current===!0&&(y(),g==null||g(),w.current=!1)},[y,g]);return s.jsx(nD,{scope:n,contentWrapper:l,shouldExpandOnScrollRef:m,onScrollButtonChange:_,children:s.jsx("div",{ref:c,style:{display:"flex",flexDirection:"column",position:"fixed",zIndex:j},children:s.jsx(be.div,{...o,ref:h,style:{boxSizing:"border-box",maxHeight:"100%",...o.style}})})})});L2.displayName=tD;var sD="SelectPopperPosition",Qp=f.forwardRef((e,t)=>{const{__scopeSelect:n,align:r="start",collisionPadding:o=Is,...a}=e,i=Uu(n);return s.jsx(h1,{...i,...a,ref:t,align:r,collisionPadding:o,style:{boxSizing:"border-box",...a.style,"--radix-select-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-select-content-available-width":"var(--radix-popper-available-width)","--radix-select-content-available-height":"var(--radix-popper-available-height)","--radix-select-trigger-width":"var(--radix-popper-anchor-width)","--radix-select-trigger-height":"var(--radix-popper-anchor-height)"}})});Qp.displayName=sD;var[nD,Hf]=Ua(vo,{}),Kp="SelectViewport",F2=f.forwardRef((e,t)=>{const{__scopeSelect:n,nonce:r,...o}=e,a=Mr(Kp,n),i=Hf(Kp,n),l=We(t,a.onViewportChange),c=f.useRef(0);return s.jsxs(s.Fragment,{children:[s.jsx("style",{dangerouslySetInnerHTML:{__html:"[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}"},nonce:r}),s.jsx(Bu.Slot,{scope:n,children:s.jsx(be.div,{"data-radix-select-viewport":"",role:"presentation",...o,ref:l,style:{position:"relative",flex:1,overflow:"hidden auto",...o.style},onScroll:le(o.onScroll,d=>{const u=d.currentTarget,{contentWrapper:h,shouldExpandOnScrollRef:p}=i;if(p!=null&&p.current&&h){const m=Math.abs(c.current-u.scrollTop);if(m>0){const w=window.innerHeight-Is*2,x=parseFloat(h.style.minHeight),b=parseFloat(h.style.height),v=Math.max(x,b);if(v0?j:0,h.style.justifyContent="flex-end")}}}c.current=u.scrollTop})})})]})});F2.displayName=Kp;var z2="SelectGroup",[rD,oD]=Ua(z2),aD=f.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,o=na();return s.jsx(rD,{scope:n,id:o,children:s.jsx(be.div,{role:"group","aria-labelledby":o,...r,ref:t})})});aD.displayName=z2;var B2="SelectLabel",W2=f.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,o=oD(B2,n);return s.jsx(be.div,{id:o.id,...r,ref:t})});W2.displayName=B2;var Vd="SelectItem",[iD,U2]=Ua(Vd),H2=f.forwardRef((e,t)=>{const{__scopeSelect:n,value:r,disabled:o=!1,textValue:a,...i}=e,l=Rr(Vd,n),c=Mr(Vd,n),d=l.value===r,[u,h]=f.useState(a??""),[p,m]=f.useState(!1),w=We(t,g=>{var y;return(y=c.itemRefCallback)==null?void 0:y.call(c,g,r,o)}),x=na(),b=f.useRef("touch"),v=()=>{o||(l.onValueChange(r),l.onOpenChange(!1))};if(r==="")throw new Error("A must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.");return s.jsx(iD,{scope:n,value:r,disabled:o,textId:x,isSelected:d,onItemTextChange:f.useCallback(g=>{h(y=>y||((g==null?void 0:g.textContent)??"").trim())},[]),children:s.jsx(Bu.ItemSlot,{scope:n,value:r,disabled:o,textValue:u,children:s.jsx(be.div,{role:"option","aria-labelledby":x,"data-highlighted":p?"":void 0,"aria-selected":d&&p,"data-state":d?"checked":"unchecked","aria-disabled":o||void 0,"data-disabled":o?"":void 0,tabIndex:o?void 0:-1,...i,ref:w,onFocus:le(i.onFocus,()=>m(!0)),onBlur:le(i.onBlur,()=>m(!1)),onClick:le(i.onClick,()=>{b.current!=="mouse"&&v()}),onPointerUp:le(i.onPointerUp,()=>{b.current==="mouse"&&v()}),onPointerDown:le(i.onPointerDown,g=>{b.current=g.pointerType}),onPointerMove:le(i.onPointerMove,g=>{var y;b.current=g.pointerType,o?(y=c.onItemLeave)==null||y.call(c):b.current==="mouse"&&g.currentTarget.focus({preventScroll:!0})}),onPointerLeave:le(i.onPointerLeave,g=>{var y;g.currentTarget===document.activeElement&&((y=c.onItemLeave)==null||y.call(c))}),onKeyDown:le(i.onKeyDown,g=>{var j;((j=c.searchRef)==null?void 0:j.current)!==""&&g.key===" "||(VO.includes(g.key)&&v(),g.key===" "&&g.preventDefault())})})})})});H2.displayName=Vd;var ki="SelectItemText",q2=f.forwardRef((e,t)=>{const{__scopeSelect:n,className:r,style:o,...a}=e,i=Rr(ki,n),l=Mr(ki,n),c=U2(ki,n),d=KO(ki,n),[u,h]=f.useState(null),p=We(t,v=>h(v),c.onItemTextChange,v=>{var g;return(g=l.itemTextRefCallback)==null?void 0:g.call(l,v,c.value,c.disabled)}),m=u==null?void 0:u.textContent,w=f.useMemo(()=>s.jsx("option",{value:c.value,disabled:c.disabled,children:m},c.value),[c.disabled,c.value,m]),{onNativeOptionAdd:x,onNativeOptionRemove:b}=d;return vt(()=>(x(w),()=>b(w)),[x,b,w]),s.jsxs(s.Fragment,{children:[s.jsx(be.span,{id:c.textId,...a,ref:p}),c.isSelected&&i.valueNode&&!i.valueNodeHasChildren?Oa.createPortal(a.children,i.valueNode):null]})});q2.displayName=ki;var V2="SelectItemIndicator",G2=f.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e;return U2(V2,n).isSelected?s.jsx(be.span,{"aria-hidden":!0,...r,ref:t}):null});G2.displayName=V2;var Xp="SelectScrollUpButton",Y2=f.forwardRef((e,t)=>{const n=Mr(Xp,e.__scopeSelect),r=Hf(Xp,e.__scopeSelect),[o,a]=f.useState(!1),i=We(t,r.onScrollButtonChange);return vt(()=>{if(n.viewport&&n.isPositioned){let l=function(){const d=c.scrollTop>0;a(d)};const c=n.viewport;return l(),c.addEventListener("scroll",l),()=>c.removeEventListener("scroll",l)}},[n.viewport,n.isPositioned]),o?s.jsx(K2,{...e,ref:i,onAutoScroll:()=>{const{viewport:l,selectedItem:c}=n;l&&c&&(l.scrollTop=l.scrollTop-c.offsetHeight)}}):null});Y2.displayName=Xp;var Jp="SelectScrollDownButton",Q2=f.forwardRef((e,t)=>{const n=Mr(Jp,e.__scopeSelect),r=Hf(Jp,e.__scopeSelect),[o,a]=f.useState(!1),i=We(t,r.onScrollButtonChange);return vt(()=>{if(n.viewport&&n.isPositioned){let l=function(){const d=c.scrollHeight-c.clientHeight,u=Math.ceil(c.scrollTop)c.removeEventListener("scroll",l)}},[n.viewport,n.isPositioned]),o?s.jsx(K2,{...e,ref:i,onAutoScroll:()=>{const{viewport:l,selectedItem:c}=n;l&&c&&(l.scrollTop=l.scrollTop+c.offsetHeight)}}):null});Q2.displayName=Jp;var K2=f.forwardRef((e,t)=>{const{__scopeSelect:n,onAutoScroll:r,...o}=e,a=Mr("SelectScrollButton",n),i=f.useRef(null),l=Wu(n),c=f.useCallback(()=>{i.current!==null&&(window.clearInterval(i.current),i.current=null)},[]);return f.useEffect(()=>()=>c(),[c]),vt(()=>{var u;const d=l().find(h=>h.ref.current===document.activeElement);(u=d==null?void 0:d.ref.current)==null||u.scrollIntoView({block:"nearest"})},[l]),s.jsx(be.div,{"aria-hidden":!0,...o,ref:t,style:{flexShrink:0,...o.style},onPointerDown:le(o.onPointerDown,()=>{i.current===null&&(i.current=window.setInterval(r,50))}),onPointerMove:le(o.onPointerMove,()=>{var d;(d=a.onItemLeave)==null||d.call(a),i.current===null&&(i.current=window.setInterval(r,50))}),onPointerLeave:le(o.onPointerLeave,()=>{c()})})}),lD="SelectSeparator",X2=f.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e;return s.jsx(be.div,{"aria-hidden":!0,...r,ref:t})});X2.displayName=lD;var Zp="SelectArrow",cD=f.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,o=Uu(n),a=Rr(Zp,n),i=Mr(Zp,n);return a.open&&i.position==="popper"?s.jsx(m1,{...o,...r,ref:t}):null});cD.displayName=Zp;var dD="SelectBubbleInput",J2=f.forwardRef(({__scopeSelect:e,value:t,...n},r)=>{const o=f.useRef(null),a=We(r,o),i=HO(t);return f.useEffect(()=>{const l=o.current;if(!l)return;const c=window.HTMLSelectElement.prototype,u=Object.getOwnPropertyDescriptor(c,"value").set;if(i!==t&&u){const h=new Event("change",{bubbles:!0});u.call(l,t),l.dispatchEvent(h)}},[i,t]),s.jsx(be.select,{...n,style:{...dN,...n.style},ref:a,defaultValue:t})});J2.displayName=dD;function Z2(e){return e===""||e===void 0}function e_(e){const t=Bs(e),n=f.useRef(""),r=f.useRef(0),o=f.useCallback(i=>{const l=n.current+i;t(l),function c(d){n.current=d,window.clearTimeout(r.current),d!==""&&(r.current=window.setTimeout(()=>c(""),1e3))}(l)},[t]),a=f.useCallback(()=>{n.current="",window.clearTimeout(r.current)},[]);return f.useEffect(()=>()=>window.clearTimeout(r.current),[]),[n,o,a]}function t_(e,t,n){const o=t.length>1&&Array.from(t).every(d=>d===t[0])?t[0]:t,a=n?e.indexOf(n):-1;let i=uD(e,Math.max(a,0));o.length===1&&(i=i.filter(d=>d!==n));const c=i.find(d=>d.textValue.toLowerCase().startsWith(o.toLowerCase()));return c!==n?c:void 0}function uD(e,t){return e.map((n,r)=>e[(t+r)%e.length])}var hD=E2,s_=I2,mD=P2,pD=O2,xD=D2,n_=R2,fD=F2,r_=W2,o_=H2,gD=q2,vD=G2,a_=Y2,i_=Q2,l_=X2;const yD=hD,bD=mD,c_=f.forwardRef(({className:e,children:t,...n},r)=>s.jsxs(s_,{ref:r,className:Me("flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",e),...n,children:[t,s.jsx(pD,{asChild:!0,children:s.jsx(Al,{className:"h-4 w-4 opacity-50"})})]}));c_.displayName=s_.displayName;const d_=f.forwardRef(({className:e,...t},n)=>s.jsx(a_,{ref:n,className:Me("flex cursor-default items-center justify-center py-1",e),...t,children:s.jsx(V4,{className:"h-4 w-4"})}));d_.displayName=a_.displayName;const u_=f.forwardRef(({className:e,...t},n)=>s.jsx(i_,{ref:n,className:Me("flex cursor-default items-center justify-center py-1",e),...t,children:s.jsx(Al,{className:"h-4 w-4"})}));u_.displayName=i_.displayName;const h_=f.forwardRef(({className:e,children:t,position:n="popper",...r},o)=>s.jsx(xD,{children:s.jsxs(n_,{ref:o,className:Me("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",n==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",e),position:n,...r,children:[s.jsx(d_,{}),s.jsx(fD,{className:Me("p-1",n==="popper"&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),children:t}),s.jsx(u_,{})]})}));h_.displayName=n_.displayName;const wD=f.forwardRef(({className:e,...t},n)=>s.jsx(r_,{ref:n,className:Me("py-1.5 pl-8 pr-2 text-sm font-semibold",e),...t}));wD.displayName=r_.displayName;const m_=f.forwardRef(({className:e,children:t,...n},r)=>s.jsxs(o_,{ref:r,className:Me("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",e),...n,children:[s.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:s.jsx(vD,{children:s.jsx(ve,{className:"h-4 w-4"})})}),s.jsx(gD,{children:t})]}));m_.displayName=o_.displayName;const jD=f.forwardRef(({className:e,...t},n)=>s.jsx(l_,{ref:n,className:Me("-mx-1 my-1 h-px bg-muted",e),...t}));jD.displayName=l_.displayName;const nm={EUR:"EUR (€)",USD:"USD ($)",GBP:"GBP (£)",AUD:"AUD (A$)"};function ND(){const{currency:e,setCurrency:t,isAutoDetected:n,isLoading:r}=S2();return s.jsxs("div",{className:"flex flex-col items-center gap-2",children:[s.jsxs("div",{className:"flex items-center justify-center gap-2",children:[s.jsx(Z4,{className:"w-4 h-4 text-octo-text"}),r?s.jsxs("div",{className:"h-10 w-32 bg-white/10 border border-white/20 text-white rounded-md flex items-center justify-between px-3","aria-hidden":!0,children:[s.jsx("span",{className:"text-sm",children:nm[e]}),s.jsx("span",{className:"text-sm opacity-70",children:"▾"})]}):s.jsx(BO,{fallback:s.jsxs("div",{className:"h-10 w-32 bg-white/10 border border-white/20 text-white rounded-md flex items-center justify-between px-3","aria-hidden":!0,children:[s.jsx("span",{className:"text-sm",children:nm[e]}),s.jsx("span",{className:"text-sm opacity-70",children:"▾"})]}),children:s.jsxs(yD,{value:e,onValueChange:o=>t(o),children:[s.jsx(c_,{className:"w-32 bg-white/10 border-white/20 text-white hover:bg-white/20",children:s.jsx(bD,{})}),s.jsx(h_,{className:"bg-octo-card border-white/20",children:_2.map(o=>s.jsx(m_,{value:o,className:"text-white hover:bg-white/10 focus:bg-white/10 focus:text-white",children:nm[o]},o))})]})})]}),n&&!r&&s.jsx("p",{className:"text-xs text-octo-text",children:"Currency detected based on your location"})]})}const kD=[{name:"Basic",period:"/month",description:"For small teams getting serious",features:["80 test cases","240 cloud runs/month","3 parallel executions","3 projects/URLs","20 AI test creations/month","max 15 steps per test case","email support"],cta:"START FREE TRIAL",highlighted:!1},{name:"Pro",period:"/month",description:"For growing teams shipping fast",features:["300 test cases","1,800 cloud runs/month","12 parallel executions","10 projects/URLs","75 AI test creations/month","max 30 steps per test case","priority support"],cta:"START FREE TRIAL",highlighted:!0},{name:"Enterprise",description:"For organizations at scale",features:["unlimited test cases","custom cloud runs","custom parallel execution","unlimited projects/URLs","custom AI test creations/month","custom max steps per test case","dedicated support","custom SLA","SOC2 compliance"],cta:"START FREE TRIAL",highlighted:!1}];function _D(){const e=OO(),{formatPrice:t,isLoading:n}=S2(),[r]=H3(),[o,a]=f.useState(!1),i=new URL("https://app.octomind.dev/");i.searchParams.set("utm_source",r.get("utm_source")||"website"),i.searchParams.set("utm_medium",r.get("utm_medium")||"pricing");const l=e.get_distinct_id();l&&i.searchParams.set("distinctId",l);const c=d=>d==="Enterprise"?"Custom":t(d.toLowerCase());return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("section",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto text-center relative z-10",children:s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"mb-6 text-5xl md:text-6xl font-black text-white",children:"simple, transparent pricing"}),s.jsx("p",{className:"max-w-2xl mx-auto text-xl font-normal text-octo-text mb-8",children:"Choose the plan that fits your team. All plans include our core AI-powered testing features."}),s.jsx(ND,{})]})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-7xl mx-auto relative z-10",children:s.jsx("div",{className:"grid md:grid-cols-3 gap-8",children:kD.map((d,u)=>s.jsxs("div",{className:`backdrop-blur-xl rounded-2xl p-8 animate-fade-in-up ${d.highlighted?"bg-octo-card/80 border-2 border-octo-purple relative":"bg-octo-card/60 border border-white/10"}`,children:[d.highlighted&&s.jsx("div",{className:"absolute -top-4 left-1/2 transform -translate-x-1/2 max-sm:block sm:hidden lg:block",children:s.jsx("span",{className:"bg-octo-purple text-white px-4 py-1 rounded-full text-sm font-semibold",children:"Most Popular"})}),s.jsxs("div",{className:"mb-6",children:[s.jsx("h3",{className:"mb-2 text-2xl font-black text-white",children:d.name}),s.jsxs("div",{className:"flex items-baseline mb-2",children:[s.jsx("span",{className:"text-5xl font-black text-white",children:c(d.name)}),d.period&&s.jsx("span",{className:"text-base font-normal text-octo-text",children:d.period})]}),s.jsx("p",{className:"text-sm font-normal text-octo-text",children:d.description})]}),s.jsx(ut,{className:`w-full mb-8 ${d.highlighted?"bg-octo-purple hover:bg-octo-purple-dark":"bg-white/10 hover:bg-white/20"} text-white px-6 py-3 rounded-xl`,onClick:()=>{window.open(i.toString(),"_blank")},children:d.cta}),s.jsx("div",{className:"space-y-4",children:d.features.map((h,p)=>s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx(ve,{className:"w-5 h-5 text-octo-purple flex-shrink-0 mt-0.5"}),s.jsx("span",{className:"text-sm font-normal text-white",children:h})]},p))})]},u))})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-7xl mx-auto relative z-10",children:[s.jsx("div",{className:"text-center mb-8",children:s.jsx(ut,{onClick:()=>a(!o),className:"bg-white/10 hover:bg-white/20 text-white px-8 py-3 rounded-xl text-sm font-normal",children:o?"Hide all features":"See all features"})}),o&&s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsxs("div",{className:"text-center mb-12",children:[s.jsx("h2",{className:"mb-4 text-4xl font-black text-white",children:"compare features"}),s.jsx("p",{className:"text-base font-normal text-octo-text",children:"See what's included in each plan"})]}),s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden",children:[s.jsxs("div",{className:"grid grid-cols-4 gap-4 p-6 border-b border-white/10 bg-octo-card/80",children:[s.jsx("div",{className:"text-sm font-bold text-white",children:"Feature"}),s.jsx("div",{className:"text-center text-sm font-bold text-white",children:"Basic"}),s.jsx("div",{className:"text-center text-sm font-bold text-white",children:"Pro"}),s.jsx("div",{className:"text-center text-sm font-bold text-white",children:"Enterprise"})]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Test Generation"}),[{name:"Visual test editor",basic:!0,pro:!0,enterprise:!0},{name:"Advanced recorder",basic:!0,pro:!0,enterprise:!0},{name:"AI test generation",basic:!0,pro:!0,enterprise:!0},{name:"Prompt improvements",basic:!0,pro:!0,enterprise:!0},{name:"Test code generation",basic:!0,pro:!0,enterprise:!0},{name:"AI recommended tests",basic:!0,pro:!0,enterprise:!0},{name:"Dependencies",basic:!0,pro:!0,enterprise:!0},{name:"Built in variables",basic:!0,pro:!0,enterprise:!0},{name:"AI batch generation",basic:!1,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Basic Steps"}),[{name:"Login flow detection",basic:!0,pro:!0,enterprise:!0},{name:"iFrame & shadow DOM",basic:!0,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Advanced Steps"}),[{name:"File upload & download",basic:!1,pro:!0,enterprise:!0},{name:"OTP flows",basic:!1,pro:!0,enterprise:!0},{name:"Email flows",basic:!1,pro:!0,enterprise:!0},{name:"Custom code",basic:!1,pro:!0,enterprise:!0},{name:"Custom API calls",basic:!1,pro:!0,enterprise:!0},{name:"Teardown",basic:!1,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Special Steps"}),[{name:"SMS flows",basic:!1,pro:!1,enterprise:!0},{name:"Salesforce support",basic:!1,pro:!1,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Test Execution"}),[{name:"Parallel execution",basic:!0,pro:!0,enterprise:!0},{name:"Cloud test execution",basic:!0,pro:!0,enterprise:!0},{name:"Local execution",basic:!0,pro:!0,enterprise:!0},{name:"CI/CD integration",basic:!0,pro:!0,enterprise:!0},{name:"CURL commands",basic:!0,pro:!0,enterprise:!0},{name:"Scheduler",basic:!0,pro:!0,enterprise:!0},{name:"Run history",basic:!0,pro:!0,enterprise:!0},{name:"Basic auth",basic:!0,pro:!0,enterprise:!0},{name:"Shared auth state",basic:!0,pro:!0,enterprise:!0},{name:"Geo specific storage",basic:!0,pro:!0,enterprise:!0},{name:"Proxy",basic:!0,pro:!0,enterprise:!0},{name:"Multiple environments",basic:!0,pro:!0,enterprise:!0},{name:"Exclude tests from environments",basic:!0,pro:!0,enterprise:!0},{name:"Custom variables",basic:!0,pro:!0,enterprise:!0},{name:"Different browsers",basic:!0,pro:!0,enterprise:!0},{name:"Different resolutions",basic:!0,pro:!0,enterprise:!0},{name:"Custom headers",basic:!0,pro:!0,enterprise:!0},{name:"Tag based sharding",basic:!0,pro:!0,enterprise:!0},{name:"Private location worker",basic:!1,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Debugging"}),[{name:"Trace files",basic:!0,pro:!0,enterprise:!0},{name:"Run inspection",basic:!0,pro:!0,enterprise:!0},{name:"Console logs",basic:!0,pro:!0,enterprise:!0},{name:"Network logs",basic:!0,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"AI Maintenance"}),[{name:"Flakiness guard",basic:!0,pro:!0,enterprise:!0},{name:"Auto locator repair",basic:!0,pro:!0,enterprise:!0},{name:"AI diagnostics",basic:!0,pro:!0,enterprise:!0},{name:"Auto test fix",basic:!1,pro:!0,enterprise:!0},{name:"Auto test expand",basic:!1,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Collaboration"}),[{name:"Team members",basic:"5 users",pro:"20 users",enterprise:"Unlimited"},{name:"Role-based access",basic:!0,pro:!0,enterprise:!0},{name:"Audit log",basic:!1,pro:!0,enterprise:!0},{name:"SSO",basic:!1,pro:!1,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:typeof d.basic=="boolean"?d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"}):s.jsx("span",{className:"text-sm font-normal text-white",children:d.basic})}),s.jsx("div",{className:"text-center",children:typeof d.pro=="boolean"?d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"}):s.jsx("span",{className:"text-sm font-normal text-white",children:d.pro})}),s.jsx("div",{className:"text-center",children:typeof d.enterprise=="boolean"?d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"}):s.jsx("span",{className:"text-sm font-normal text-white",children:d.enterprise})})]},u))]}),s.jsxs("div",{className:"p-6 border-b border-white/10",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Reporting"}),[{name:"Slack integration",basic:!0,pro:!0,enterprise:!0},{name:"Github integration",basic:!0,pro:!0,enterprise:!0},{name:"Test reports",basic:!0,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]}),s.jsxs("div",{className:"p-6",children:[s.jsx("h3",{className:"mb-4 text-base font-bold text-white",children:"Platform"}),[{name:"MCP server",basic:!0,pro:!0,enterprise:!0},{name:"Octomind API",basic:!0,pro:!0,enterprise:!0},{name:"Integrations",basic:!0,pro:!0,enterprise:!0},{name:"Activity log",basic:!0,pro:!0,enterprise:!0},{name:"Webhooks",basic:!0,pro:!0,enterprise:!0},{name:"Tagging",basic:!0,pro:!0,enterprise:!0},{name:"Blocking tests",basic:!0,pro:!0,enterprise:!0}].map((d,u)=>s.jsxs("div",{className:"grid grid-cols-4 gap-4 py-3 items-center",children:[s.jsx("div",{className:"text-sm font-normal text-octo-text",children:d.name}),s.jsx("div",{className:"text-center",children:d.basic?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.pro?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})}),s.jsx("div",{className:"text-center",children:d.enterprise?s.jsx(ve,{className:"w-5 h-5 text-octo-purple mx-auto"}):s.jsx("span",{className:"text-sm font-normal text-gray-600",children:"—"})})]},u))]})]})]})]})}),s.jsx(se,{})]})}function CD(){return s.jsx(zO,{children:s.jsx(_D,{})})}function kn({textToCopy:e,className:t=""}){const[n,r]=f.useState(!1),o=async()=>{try{await navigator.clipboard.writeText(e),r(!0),setTimeout(()=>r(!1),2e3)}catch(a){console.error("Failed to copy:",a)}};return s.jsx("button",{onClick:o,className:`flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-white/5 hover:bg-white/10 border border-white/10 transition-colors text-xs text-white/70 hover:text-white ${t}`,children:n?s.jsxs(s.Fragment,{children:[s.jsx("svg",{className:"w-3.5 h-3.5 text-octo-teal",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})}),s.jsx("span",{children:"Copied!"})]}):s.jsxs(s.Fragment,{children:[s.jsx("svg",{className:"w-3.5 h-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})}),s.jsx("span",{children:"Copy"})]})})}const SD=()=>{const e=Fn(),t=`# user-login.yaml id: 5fdcdbbd-19d6-5467-9288-191dd7ed3fcc description: user-login elements: - interaction: action: ENTER_TEXT calledWith: $OCTO_USERNAME selectors: - selectorType: ROLE selector: textbox options: name: Account name or email ignoreFailure: false - interaction: action: ENTER_TEXT calledWith: $OCTO_PASSWORD selectors: - selectorType: ROLE selector: textbox options: name: Password`;return s.jsx("section",{className:"pt-32 pb-20 relative overflow-hidden",children:s.jsx("div",{className:"max-w-7xl mx-auto px-4 md:px-6 relative z-10",children:s.jsxs("div",{className:"flex flex-col items-center text-center",children:[s.jsx("div",{className:"inline-flex items-center px-4 py-1.5 rounded-full border border-octo-purple/50 bg-octo-purple/10 backdrop-blur-sm mb-8",children:s.jsx("span",{className:"text-sm font-medium text-octo-purple-light",children:"New: DEV Mode"})}),s.jsx("h1",{className:"text-4xl md:text-5xl lg:text-6xl font-bold mb-6 max-w-4xl",children:s.jsx("span",{className:"bg-gradient-to-r from-octo-purple via-octo-blue to-octo-teal bg-clip-text text-transparent",children:"treat your tests like code"})}),s.jsx("p",{className:"text-xl text-octo-text max-w-2xl mb-10",children:"Bring end-to-end testing into your git workflow. Version control, review, and edit your tests locally."}),s.jsxs("div",{className:"flex flex-col sm:flex-row items-center gap-4 mb-16",children:[s.jsx(ut,{className:"px-8 py-6 text-base font-medium rounded-xl bg-octo-purple hover:bg-octo-purple-dark text-white shadow-lg shadow-octo-purple/25",onClick:()=>window.open("https://octomind.dev/docs/dev-mode","_blank"),children:"Read the Docs"}),s.jsx(ut,{variant:"outline",className:"px-8 py-6 text-base font-medium rounded-xl bg-transparent border-white/20 text-white hover:bg-white/10 hover:border-white/30",onClick:()=>e("/pricing"),children:"Get Started"})]}),s.jsx("div",{className:"w-full max-w-5xl relative",children:s.jsx("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-2 md:p-4 hover:border-white/20 transition-all",children:s.jsxs("div",{className:"grid md:grid-cols-2 gap-2 md:gap-4",children:[s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsxs("div",{className:"flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded-t text-sm",children:[s.jsx("svg",{className:"w-4 h-4 text-[#e6a700]",viewBox:"0 0 24 24",fill:"currentColor",children:s.jsx("path",{d:"M2 6a2 2 0 012-2h5l2 2h9a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"})}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"user-login.yaml"})]})]}),s.jsx(kn,{textToCopy:t})]}),s.jsx("div",{className:"p-4 overflow-x-auto",children:s.jsx("pre",{className:"text-xs md:text-sm font-mono leading-relaxed",children:s.jsx("code",{children:t.split(` `).map((r,o)=>s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:o+1}),s.jsx("span",{className:r.startsWith("#")?"text-white/40":r.includes(":")&&!r.includes("{{")?"text-octo-blue":r.includes("{{")?"text-octo-purple":"text-white/80",children:r||" "})]},o))})})})]}),s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"Terminal"})]}),s.jsx(kn,{textToCopy:"octomind pull"})]}),s.jsx("div",{className:"p-4 overflow-x-auto text-left",children:s.jsx("pre",{className:"text-xs md:text-sm leading-relaxed text-left",style:{fontFamily:"'Fira Code', 'Menlo', 'Monaco', monospace"},children:s.jsxs("code",{className:"block space-y-1 text-left",children:[s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"text-octo-teal",children:"octomind pull"})]}),s.jsx("div",{className:"h-1"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"✔"}),s.jsx("span",{className:"text-white/70",children:" Authenticating with Octomind..."})]}),s.jsxs("div",{children:[s.jsx("span",{className:"text-cyan-400",children:"ℹ"}),s.jsx("span",{className:"text-white/70",children:" Fetching test configurations..."})]}),s.jsx("div",{className:"h-1"}),s.jsxs("div",{className:"text-white/70",children:[s.jsx("span",{className:"text-white/80",children:"user-login.yaml"}),s.jsx("span",{className:"text-white/40",children:" "}),s.jsx("span",{className:"text-green-400",children:"[####################]"}),s.jsx("span",{className:"text-white/70",children:" 100%"})]}),s.jsxs("div",{className:"text-white/70",children:[s.jsx("span",{className:"text-white/80",children:"checkout-flow.yaml"}),s.jsx("span",{className:"text-white/40",children:" "}),s.jsx("span",{className:"text-green-400",children:"[####################]"}),s.jsx("span",{className:"text-white/70",children:" 100%"})]}),s.jsxs("div",{className:"text-white/70",children:[s.jsx("span",{className:"text-white/80",children:"signup-validation.yaml"}),s.jsx("span",{className:"text-white/40",children:" "}),s.jsx("span",{className:"text-green-400",children:"[####################]"}),s.jsx("span",{className:"text-white/70",children:" 100%"})]}),s.jsx("div",{className:"h-1"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"✔"}),s.jsx("span",{className:"text-white/70",children:" Success! "}),s.jsx("span",{className:"text-white/80",children:"3 test cases"}),s.jsx("span",{className:"text-white/70",children:" synced to "}),s.jsx("span",{className:"text-cyan-400",children:"./.octomind"})]}),s.jsx("div",{className:"h-1"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"inline-block w-2 h-4 bg-white/80 animate-pulse"})]})]})})})]})]})})})]})})})},ED=()=>{const e=`steps: id: 5fdcdbbd-19d6-5467-9288-191dd7ed3fcc description: user-login elements: - interaction: action: ENTER_TEXT calledWith: $OCTO_USERNAME selectors: - selectorType: ROLE selector: textbox options: name: Account name or email ignoreFailure: false - interaction: action: ENTER_TEXT calledWith: $OCTO_PASSWORD selectors: - selectorType: ROLE selector: textbox options: name: Password `,t=[{number:"01",title:"Initialize",description:"Pull your AI-generated tests into your repo as readable YAML.",visual:"terminal"},{number:"02",title:"Commit",description:"Check the generated test files into your repository. They are now part of your codebase version history.",visual:"commit"},{number:"03",title:"Edit",description:"Need to tweak a selector? The AI does the heavy lifting, but the YAML exports give you full granular control to edit manually when you want.",visual:"code"},{number:"04",title:"Run Locally",description:"Debug instantly. Run any test case against your local host or staging URL to verify changes before you push.",visual:"verify"},{number:"05",title:"Sync",description:"Push to Main. Your CI/CD pipeline syncs the definitions back to Octomind and executes them.",visual:"sync"}];return s.jsx("section",{className:"py-24 relative",children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6",children:[s.jsx("div",{className:"text-center mb-16",children:s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white",children:"AI-generated, developer controlled"})}),s.jsxs("div",{className:"relative max-w-4xl mx-auto",children:[s.jsx("div",{className:"absolute left-8 md:left-12 top-8 bottom-8 w-px bg-gradient-to-b from-octo-purple/50 via-white/20 to-octo-blue/50"}),s.jsx("div",{className:"space-y-16",children:t.map((n,r)=>s.jsxs("div",{className:"relative flex gap-6 md:gap-10",children:[s.jsx("div",{className:"relative z-10 flex-shrink-0",children:s.jsx("div",{className:"w-16 md:w-24 h-16 md:h-24 rounded-full bg-gradient-to-br from-octo-purple/20 to-octo-blue/20 border border-white/10 flex items-center justify-center backdrop-blur-sm",children:s.jsx("span",{className:"text-xl md:text-2xl font-bold text-white/80",children:n.number})})}),s.jsxs("div",{className:"flex-1 pt-2 md:pt-4",children:[s.jsx("h3",{className:"text-2xl md:text-3xl font-bold text-white mb-3",children:n.title}),s.jsx("p",{className:"text-lg text-octo-text mb-6 max-w-xl",children:n.description}),n.visual==="terminal"&&s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 max-w-md",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"Terminal"})]}),s.jsx(kn,{textToCopy:"octomind pull"})]}),s.jsx("div",{className:"p-4",children:s.jsx("pre",{className:"text-sm md:text-base font-mono",style:{fontFamily:"'Fira Code', 'Menlo', 'Monaco', monospace"},children:s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"text-octo-teal",children:"octomind pull"})]})})})]}),n.visual==="commit"&&s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 max-w-xl",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"Terminal"})]}),s.jsx(kn,{textToCopy:"git commit -m"})]}),s.jsx("div",{className:"p-4",children:s.jsx("pre",{className:"text-sm md:text-base font-mono",style:{fontFamily:"'Fira Code', 'Menlo', 'Monaco', monospace"},children:s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"text-octo-teal",children:"git commit"}),s.jsx("span",{className:"text-octo-teal",children:" -m"})]})})})]}),n.visual==="code"&&s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 max-w-lg",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsxs("div",{className:"flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded-t text-sm",children:[s.jsx("svg",{className:"w-4 h-4 text-[#e6a700]",viewBox:"0 0 24 24",fill:"currentColor",children:s.jsx("path",{d:"M2 6a2 2 0 012-2h5l2 2h9a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"})}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"user-login.yaml"})]})]}),s.jsx(kn,{textToCopy:e})]}),s.jsx("div",{className:"p-4 overflow-x-auto",children:s.jsx("pre",{className:"text-sm font-mono leading-relaxed",children:s.jsx("code",{children:e.split(` `).map((o,a)=>{const i=o.includes("name: Account name or email");return s.jsxs("div",{className:`flex ${i?"bg-octo-purple/20 -mx-4 px-4 border-l-2 border-octo-purple":""}`,children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:a+1}),s.jsx("span",{className:i?"text-octo-purple-light font-medium":o.includes(":")?"text-octo-blue":"text-white/70",children:o||" "})]},a)})})})})]}),n.visual==="verify"&&s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 max-w-xl",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"Terminal"})]}),s.jsx(kn,{textToCopy:"octomind execute-local"})]}),s.jsx("div",{className:"p-4 overflow-x-auto text-left",children:s.jsx("pre",{className:"text-xs md:text-sm leading-relaxed text-left",style:{fontFamily:"'Fira Code', 'Menlo', 'Monaco', monospace"},children:s.jsx("code",{className:"block text-left",children:s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"text-octo-teal",children:"octomind execute-local"})]})})})})]}),n.visual==="sync"&&s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 max-w-xl",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"Terminal"})]}),s.jsx(kn,{textToCopy:"git push origin main"})]}),s.jsx("div",{className:"p-4",children:s.jsx("pre",{className:"text-sm md:text-base font-mono space-y-1",style:{fontFamily:"'Fira Code', 'Menlo', 'Monaco', monospace"},children:s.jsxs("div",{children:[s.jsx("span",{className:"text-green-400",children:"➜"}),s.jsx("span",{className:"text-white/80",children:" my-web-app "}),s.jsx("span",{className:"text-cyan-400",children:"git:("}),s.jsx("span",{className:"text-red-400",children:"main"}),s.jsx("span",{className:"text-cyan-400",children:")"}),s.jsx("span",{className:"text-white/80",children:" "}),s.jsx("span",{className:"text-octo-teal",children:"git push origin main"})]})})})]})]})]},n.number))})]})]})})},TD=()=>s.jsx("section",{className:"py-24 relative",children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-6",children:"build. test. ship. without leaving your IDE"}),s.jsx("p",{className:"text-lg md:text-xl text-octo-text max-w-3xl mx-auto",children:"Dev Mode bridges the gap between AI-generated testing and your local development workflow."})]}),s.jsxs("div",{className:"flex flex-col gap-6 max-w-6xl mx-auto",children:[s.jsx("div",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-6 md:p-8 transition-all duration-300 hover:border-white/20 hover:-translate-y-1 hover:shadow-xl hover:shadow-octo-purple/10",children:s.jsxs("div",{className:"flex flex-col md:flex-row md:items-start md:gap-8",children:[s.jsxs("div",{className:"flex-1 mb-6 md:mb-0",children:[s.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[s.jsx(X4,{className:"w-5 h-5 text-octo-teal"}),s.jsx("h3",{className:"text-xl font-bold text-white",children:"Atomic Commits & Reviews"})]}),s.jsx("p",{className:"text-octo-teal font-medium mb-2",children:"Test versioning, done right."}),s.jsx("p",{className:"text-sm text-octo-text leading-relaxed",children:"Update your tests in the same PR as your feature. Review YAML changes alongside code changes."})]}),s.jsxs("div",{className:"flex-1 relative overflow-hidden rounded-lg bg-[#0d0f14] border border-white/10",children:[s.jsxs("div",{className:"flex items-center gap-2 px-4 py-2.5 bg-[#161b22] border-b border-white/10",children:[s.jsx(ta,{className:"w-4 h-4 text-white/50"}),s.jsx("span",{className:"text-xs text-white/80 font-mono",children:".octomind/user-login.yaml"})]}),s.jsxs("div",{className:"grid grid-cols-2 divide-x divide-white/10",children:[s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"px-3 py-1.5 text-[10px] text-white/40 font-mono bg-[#0d0f14] border-b border-white/5",children:"Before"}),s.jsx("div",{className:"bg-[#3d1f1f] px-3 py-2",children:s.jsxs("code",{className:"text-xs font-mono text-[#f85149] whitespace-nowrap",children:[s.jsx("span",{className:"text-[#f85149]/60 mr-2",children:"-"}),'selector: button[name="submit"]']})})]}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"px-3 py-1.5 text-[10px] text-white/40 font-mono bg-[#0d0f14] border-b border-white/5",children:"After"}),s.jsxs("div",{className:"bg-[#1f3d1f] px-3 py-2 relative",children:[s.jsxs("code",{className:"text-xs font-mono text-[#3fb950] whitespace-nowrap",children:[s.jsx("span",{className:"text-[#3fb950]/60 mr-2",children:"+"}),'selector: button[data-testid="login-btn"]']}),s.jsx("div",{className:"absolute right-2 top-1/2 -translate-y-1/2 w-6 h-6 rounded-full bg-octo-blue flex items-center justify-center shadow-lg",children:s.jsx("svg",{className:"w-3.5 h-3.5 text-white",fill:"currentColor",viewBox:"0 0 16 16",children:s.jsx("path",{d:"M2.5 1A1.5 1.5 0 001 2.5v8A1.5 1.5 0 002.5 12h1.793L8 15.707 11.707 12H13.5A1.5 1.5 0 0015 10.5v-8A1.5 1.5 0 0013.5 1h-11z"})})})]})]})]})]})]})}),s.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[s.jsxs("div",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-6 transition-all duration-300 hover:border-white/20 hover:-translate-y-1 hover:shadow-xl hover:shadow-octo-purple/10",children:[s.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[s.jsx(ta,{className:"w-5 h-5 text-octo-teal"}),s.jsx("h3",{className:"text-lg font-bold text-white",children:"Your Editor, Your Rules"})]}),s.jsx("p",{className:"text-sm text-octo-text leading-relaxed mb-4",children:"Edit tests with full schema validation, linting, and autocomplete."}),s.jsxs("div",{className:"rounded-lg bg-[#1e1e1e] border border-white/10 overflow-hidden",children:[s.jsxs("div",{className:"flex bg-[#252526] border-b border-white/5",children:[s.jsx("div",{className:"px-3 py-1.5 text-[10px] font-mono text-white/40 border-r border-white/5",children:"App.tsx"}),s.jsx("div",{className:"px-3 py-1.5 text-[10px] font-mono text-white/40 border-r border-white/5",children:"auth.ts"}),s.jsx("div",{className:"px-3 py-1.5 text-[10px] font-mono text-white bg-[#1e1e1e] border-r border-white/5",children:"user-login.yaml"})]}),s.jsxs("div",{className:"p-3 text-[10px] font-mono leading-relaxed relative",children:[s.jsxs("div",{children:[s.jsx("span",{className:"text-[#ce9178]",children:"description"}),s.jsx("span",{className:"text-white/60",children:":"})," ",s.jsx("span",{className:"text-[#6a9955]",children:"user-login"})]}),s.jsxs("div",{children:[s.jsx("span",{className:"text-[#ce9178]",children:"elements"}),s.jsx("span",{className:"text-white/60",children:":"})]}),s.jsxs("div",{className:"relative",children:[s.jsx("span",{className:"text-white/40 ml-2",children:"- "}),s.jsx("span",{className:"text-[#9cdcfe]",children:"interaction"}),s.jsx("span",{className:"text-white/60",children:":"}),s.jsxs("div",{className:"absolute left-16 top-4 bg-[#252526] border border-[#454545] rounded shadow-xl z-10 w-36",children:[s.jsx("div",{className:"px-2 py-1 text-[9px] border-b border-white/10",children:s.jsx("span",{className:"text-[#4ec9b0]",children:"InteractionType"})}),s.jsxs("div",{className:"px-2 py-1 text-[9px] text-white/60",children:["action: ",s.jsx("span",{className:"text-[#4ec9b0]",children:"Action"})]})]})]}),s.jsxs("div",{children:[s.jsx("span",{className:"text-white/40 ml-6",children:"action"}),s.jsx("span",{className:"text-white/60",children:":"})," ",s.jsx("span",{className:"text-[#dcdcaa]",children:"CLICK"})]})]})]})]}),s.jsxs("div",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-6 transition-all duration-300 hover:border-white/20 hover:-translate-y-1 hover:shadow-xl hover:shadow-octo-purple/10",children:[s.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[s.jsx("div",{className:"w-5 h-5 rounded bg-octo-teal/20 flex items-center justify-center",children:s.jsx("span",{className:"text-octo-teal text-xs font-mono font-bold",children:"$"})}),s.jsx("h3",{className:"text-lg font-bold text-white",children:"Zero Drift"})]}),s.jsx("p",{className:"text-sm text-octo-text leading-relaxed mb-4",children:"Pull latest tests or push local changes in seconds."}),s.jsxs("div",{className:"bg-[#0d0f14] rounded-lg border border-white/10 overflow-hidden",children:[s.jsxs("div",{className:"flex items-center gap-1.5 px-3 py-1.5 bg-[#1a1d27] border-b border-white/5",children:[s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-[#28c840]"})]}),s.jsxs("div",{className:"p-3 text-[10px] font-mono space-y-1",children:[s.jsxs("div",{children:[s.jsx("span",{className:"text-octo-teal",children:"➜"}),s.jsx("span",{className:"text-white/60",children:" my-app "}),s.jsx("span",{className:"text-octo-blue",children:"git:("}),s.jsx("span",{className:"text-[#f85149]",children:"main"}),s.jsx("span",{className:"text-octo-blue",children:")"}),s.jsx("span",{className:"text-white",children:" octomind pull"})]}),s.jsx("div",{className:"text-octo-teal",children:"✔ Fetched 12 test cases"}),s.jsx("div",{className:"text-octo-teal",children:"✔ Updated .octomind/manifest.json"}),s.jsx("div",{className:"text-[#febc2e]",children:"✨ Ready"})]})]})]})]})]})]})}),ID=()=>{const e=`name: Octomind Dev Mode Sync on: push: branches: - main # or your default branch jobs: sync-tests: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Execute tests on Octomind uses: OctoMind-dev/automagically-action-execute@v2 with: url: token: \${{ secrets.OCTOMIND_API_KEY }} testTargetId: `,t=[{title:"Sync",description:"Uploads local test changes to the platform."},{title:"Verify",description:"Runs the updated suite against your deployment."}],n=[13,14];return s.jsx("section",{className:"py-24 relative overflow-hidden",children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-4",children:"automated sync & execution"}),s.jsx("p",{className:"text-lg md:text-xl text-octo-text max-w-3xl mx-auto",children:"Keep your platform tests in lockstep with your repository. This workflow pushes your latest YAML definitions to Octomind and triggers a regression suite every time you merge to main."})]}),s.jsxs("div",{className:"grid md:grid-cols-2 gap-8 md:gap-12 items-center",children:[s.jsxs("div",{children:[s.jsx("h3",{className:"text-2xl md:text-3xl font-bold text-white mb-4",children:"Post-Merge Synchronization"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Add this workflow to run immediately after a merge to main."}),s.jsx("div",{className:"space-y-4",children:t.map((r,o)=>s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx("div",{className:"flex-shrink-0 w-5 h-5 rounded-full bg-octo-teal/20 flex items-center justify-center mt-0.5",children:s.jsx("svg",{className:"w-3 h-3 text-octo-teal",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:3,children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})})}),s.jsxs("div",{children:[s.jsxs("span",{className:"font-semibold text-white",children:[r.title,":"]})," ",s.jsx("span",{className:"text-octo-text",children:r.description})]})]},o))})]}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"absolute inset-0 bg-gradient-to-br from-octo-purple/20 to-octo-blue/10 rounded-xl blur-2xl -z-10 scale-110"}),s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10 shadow-2xl",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsxs("div",{className:"flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded-t text-sm",children:[s.jsx("svg",{className:"w-4 h-4 text-[#e6a700]",viewBox:"0 0 24 24",fill:"currentColor",children:s.jsx("path",{d:"M2 6a2 2 0 012-2h5l2 2h9a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"})}),s.jsx("span",{className:"text-white/80 font-mono text-xs",children:"github-actions.yml"})]})]}),s.jsx(kn,{textToCopy:e})]}),s.jsx("div",{className:"p-4 md:p-6 overflow-x-auto",children:s.jsx("pre",{className:"text-xs md:text-sm font-mono leading-relaxed",children:s.jsx("code",{children:e.split(` `).map((r,o)=>{const a=n.includes(o),i=l=>{var c,d;if(!l.trim())return s.jsx("span",{children:" "});if(l.includes("#")){const[u,...h]=l.split("#"),p="#"+h.join("#");return s.jsxs(s.Fragment,{children:[i(u),s.jsx("span",{className:"text-[#6a9955]",children:p})]})}if(l.includes("<")&&l.includes(">")){const u=l.match(/^(\s*)([\w-]+)(:)\s*(<[^>]+>)$/);if(u)return s.jsxs(s.Fragment,{children:[s.jsx("span",{children:u[1]}),s.jsx("span",{className:"text-[#e5c07b]",children:u[2]}),s.jsxs("span",{className:"text-white/80",children:[u[3]," "]}),s.jsx("span",{className:"bg-white/10 px-1 rounded text-[#98c379]",children:u[4]})]})}if(l.includes("${{")){const u=l.match(/^(\s*)([\w-]+)(:)\s*(\$\{\{[^}]+\}\})$/);if(u)return s.jsxs(s.Fragment,{children:[s.jsx("span",{children:u[1]}),s.jsx("span",{className:"text-[#e5c07b]",children:u[2]}),s.jsxs("span",{className:"text-white/80",children:[u[3]," "]}),s.jsx("span",{className:"text-[#98c379]",children:u[4]})]})}if(l.includes(":")){const u=l.indexOf(":"),h=l.substring(0,u),p=l.substring(u),m=((c=h.match(/^(\s*)/))==null?void 0:c[1])||"",w=h.trim(),x=p.substring(1).trim();if(w.startsWith("-")){const b=w.substring(1).trim();if(b)return s.jsxs(s.Fragment,{children:[s.jsx("span",{children:m}),s.jsx("span",{className:"text-white/80",children:"- "}),s.jsx("span",{className:"text-[#e5c07b]",children:b}),s.jsx("span",{className:"text-white/80",children:":"}),x&&s.jsxs("span",{className:"text-[#98c379]",children:[" ",x]})]})}return s.jsxs(s.Fragment,{children:[s.jsx("span",{children:m}),s.jsx("span",{className:"text-[#e5c07b]",children:w}),s.jsx("span",{className:"text-white/80",children:":"}),x&&s.jsxs("span",{className:"text-[#98c379]",children:[" ",x]})]})}if(l.trim().startsWith("-")){const u=((d=l.match(/^(\s*)/))==null?void 0:d[1])||"",h=l.trim().substring(1).trim();return s.jsxs(s.Fragment,{children:[s.jsx("span",{children:u}),s.jsx("span",{className:"text-white/80",children:"- "}),s.jsx("span",{className:"text-[#98c379]",children:h})]})}return s.jsx("span",{className:"text-white/80",children:l})};return s.jsxs("div",{className:`flex ${a?"bg-orange-500/10 -mx-4 md:-mx-6 px-4 md:px-6":""}`,children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:o+1}),s.jsx("span",{children:i(r)})]},o)})})})})]})]})]})]})})},AD=()=>{const e=[{question:"Can I manually edit the selectors in the YAML?",answer:"Yes. The YAML files are standard and readable. You can tweak selectors, add assertions, or modify interactions using your IDE, and version control those changes just like application code."},{question:"Can I run tests on my machine before pushing?",answer:"Yes. You can use the command octomind execute-local to run any YAML test case directly against your localhost or staging URL. This allows you to debug and verify changes instantly without waiting for the CI pipeline."},{question:"Do I need to change my existing Pull Request workflow?",answer:'No. Your existing PR checks can stay the same (validating builds). DEV mode adds a new step: a "Post-Merge" workflow that runs only after code lands in main to keep the platform synchronized.'},{question:"What happens if I edit the YAML file locally?",answer:"Once you commit and push your changes to the main branch, your CI/CD pipeline (via the post-merge workflow) will automatically push the updated definitions to Octomind and execute them."},{question:"I updated a test in the Octomind UI. Is it automatically in my repo?",answer:"No. In DEV mode, your Git repository is the single source of truth. If you create or edit tests in the UI, you must run octomind pull to fetch those changes into your local environment."}];return s.jsx("section",{id:"dev-mode-faq",className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"mb-6 text-4xl md:text-5xl font-black text-white",children:"frequently asked questions"}),s.jsx("p",{className:"text-xl font-normal text-white",children:"Everything you need to know about DEV mode"})]}),s.jsx("div",{className:"space-y-4",children:e.map((t,n)=>s.jsxs("details",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden hover:border-white/20 transition-all duration-300",children:[s.jsxs("summary",{className:"w-full px-6 md:px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden",children:[s.jsx("span",{className:"pr-8 text-sm font-normal text-white",children:t.question}),s.jsx("svg",{className:"w-6 h-6 text-octo-purple flex-shrink-0 transition-transform duration-300 group-open:rotate-180",strokeWidth:2,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),s.jsx("div",{className:"px-6 md:px-8 pb-6 leading-relaxed text-sm font-normal text-white",children:t.answer})]},n))})]})})};function PD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx(SD,{}),s.jsx(Ol,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx(ED,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx(TD,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx(ID,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx(AD,{}),s.jsx(se,{})]})}const qf="/assets/octomind-logo-symbol-D_1ZVUef.png";function OD(){const e=Fn();return s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsxs("main",{className:"relative z-10",children:[s.jsx("section",{className:"pt-32 pb-20 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsxs("h1",{className:"text-4xl md:text-6xl lg:text-7xl font-black mb-8 leading-[0.95] tracking-tight",children:[s.jsx("span",{className:"text-transparent bg-clip-text bg-gradient-to-r from-octo-purple via-octo-blue to-octo-teal",children:"self-healing"}),s.jsx("br",{}),s.jsx("span",{className:"text-white",children:"playwright tests"})]}),s.jsxs("p",{className:"text-lg md:text-xl text-octo-text max-w-3xl mx-auto leading-relaxed",children:["Stop debugging broken selectors. Octomind detects UI changes and generates the fix.",s.jsx("br",{className:"hidden md:block"}),"Just run"," ",s.jsx("code",{className:"text-octo-purple-light font-mono bg-octo-purple/15 px-2.5 py-1 rounded-md border border-octo-purple/20",children:"octomind pull"})," ","to sync the updated code to your repo."]})]}),s.jsxs("div",{className:"max-w-xl mx-auto relative mb-12",children:[s.jsx("div",{className:"absolute -inset-4 bg-gradient-to-b from-octo-purple/20 via-transparent to-octo-teal/20 blur-3xl opacity-50 pointer-events-none"}),s.jsx("div",{className:"absolute left-[39px] top-8 bottom-8 w-px bg-gradient-to-b from-red-500/40 via-octo-purple/50 to-octo-teal/40 z-0"}),s.jsxs("div",{className:"relative z-10 rounded-2xl border border-white/10 bg-gradient-to-b from-white/[0.08] to-white/[0.02] backdrop-blur-xl shadow-2xl shadow-black/50 overflow-hidden",children:[s.jsx("div",{className:"p-6 border-b border-white/10",children:s.jsxs("div",{className:"flex items-start gap-4",children:[s.jsx("div",{className:"w-11 h-11 rounded-full bg-red-500/15 flex items-center justify-center flex-shrink-0 border border-red-500/30 shadow-lg shadow-red-500/10",children:s.jsx(G4,{className:"w-5 h-5 text-red-400"})}),s.jsxs("div",{className:"flex-1 min-w-0 pt-1",children:[s.jsx("p",{className:"text-white/70 text-sm font-medium mb-2",children:"Test Failed: Element not visible"}),s.jsxs("p",{className:"font-mono text-xs text-white/40",children:["selector:"," ",s.jsx("span",{className:"line-through decoration-red-400/70 text-red-400/60",children:"'#btn-save'"})]})]})]})}),s.jsxs("div",{className:"p-6 border-b border-white/10 relative overflow-hidden",children:[s.jsx("div",{className:"absolute inset-0 bg-gradient-to-r from-octo-purple/10 via-octo-purple/15 to-octo-purple/10 pointer-events-none"}),s.jsx("div",{className:"absolute -left-20 -right-20 top-1/2 -translate-y-1/2 h-32 bg-octo-purple/20 blur-3xl pointer-events-none"}),s.jsxs("div",{className:"flex items-start gap-4 relative",children:[s.jsx("div",{className:"w-11 h-11 rounded-full bg-octo-purple/20 flex items-center justify-center flex-shrink-0 border border-octo-purple/40 shadow-lg shadow-octo-purple/30 p-2",children:s.jsx("img",{src:qf,alt:"Octomind",className:"w-full h-full object-contain"})}),s.jsxs("div",{className:"flex-1 min-w-0 pt-1",children:[s.jsx("p",{className:"text-octo-purple-light font-semibold text-sm mb-3",children:"✨ Octomind Proposed Fix"}),s.jsxs("div",{className:"rounded-lg bg-black/50 border border-white/10 p-3 font-mono text-xs shadow-inner",children:[s.jsx("span",{className:"text-green-400 font-semibold",children:"+ "}),s.jsx("span",{className:"text-white/50",children:"selector: "}),s.jsx("span",{className:"text-green-400",children:`'[data-testid="save-changes"]'`})]})]})]})]}),s.jsx("div",{className:"p-6",children:s.jsxs("div",{className:"flex items-center gap-4",children:[s.jsx("div",{className:"w-11 h-11 rounded-full border-2 border-octo-teal/50 flex items-center justify-center flex-shrink-0 bg-octo-teal/10 shadow-lg shadow-octo-teal/20",children:s.jsx(Y4,{className:"w-5 h-5 text-octo-teal"})}),s.jsxs("div",{className:"flex-1 min-w-0",children:[s.jsx("p",{className:"text-white font-semibold text-sm font-mono",children:"Checkout.yaml"}),s.jsx("p",{className:"text-white/50 text-xs mt-0.5",children:"Ready to sync • 1 file modified"})]}),s.jsx("div",{className:"px-3 py-1.5 rounded-md border border-octo-teal/30 bg-black/60 text-octo-teal text-xs font-mono animate-pulse",children:"waiting for pull"})]})})]})]}),s.jsxs("div",{className:"flex flex-col sm:flex-row items-center justify-center gap-4",children:[s.jsx(ut,{size:"lg",className:"bg-octo-purple hover:bg-octo-purple/90 text-white px-8 py-6 rounded-xl shadow-2xl shadow-octo-purple/30 transition-all hover:scale-105 hover:shadow-octo-purple/40",onClick:()=>e("/pricing"),children:"See Auto-Maintenance in Action"}),s.jsx(ut,{variant:"outline",size:"lg",className:"px-8 py-6 rounded-xl border-white/20 hover:bg-white/5 hover:border-white/30 transition-all",onClick:()=>window.open("https://octomind.dev/docs/maintain-tests/auto-fix","_blank"),children:"Read the Docs"})]})]})}),s.jsx(Ol,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{className:"py-20 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsx("h2",{className:"text-3xl md:text-4xl font-black text-center mb-16",children:"how auto-maintenance works"}),s.jsxs("div",{className:"relative",children:[s.jsxs("div",{className:"hidden md:block absolute top-[88px] left-[16.67%] right-[16.67%] h-[2px] z-0",children:[s.jsx("div",{className:"w-full h-full bg-gradient-to-r from-red-500/50 via-octo-purple/60 to-octo-teal/50 blur-sm"}),s.jsx("div",{className:"absolute inset-0 bg-gradient-to-r from-red-500/30 via-octo-purple/40 to-octo-teal/30"})]}),s.jsxs("div",{className:"grid md:grid-cols-3 gap-4 relative z-10",children:[s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm flex flex-col",children:[s.jsx("div",{className:"mb-4",children:s.jsx("span",{className:"inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-red-500/15 text-red-400 border border-red-500/20",children:"Input: Trace & Snapshot"})}),s.jsx("div",{className:"mb-5 mx-auto w-full max-w-[200px] h-[100px] flex items-center",children:s.jsxs("div",{className:"rounded-lg bg-[#0d1117] border border-white/10 overflow-hidden shadow-lg w-full",children:[s.jsxs("div",{className:"flex items-center gap-1.5 px-3 py-2 bg-[#161b22] border-b border-white/10",children:[s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-red-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-yellow-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-green-500/70"})]}),s.jsxs("div",{className:"p-3 font-mono text-[10px] space-y-1",children:[s.jsx("div",{className:"text-white/50",children:"$ npx playwright test"}),s.jsx("div",{className:"text-red-400",children:"✗ login.spec.ts:24"}),s.jsx("div",{className:"text-red-400/70 text-[9px]",children:"Element not found"})]})]})}),s.jsxs("div",{className:"mt-auto",children:[s.jsx("h3",{className:"text-xl font-bold mb-3 text-center",children:"1. Failure Analysis"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed text-center",children:"When a test fails, we analyze the Playwright Trace and DOM snapshot to distinguish between actual bugs and UI changes."})]})]}),s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm flex flex-col",children:[s.jsx("div",{className:"mb-4",children:s.jsx("span",{className:"inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-octo-purple/15 text-octo-purple-light border border-octo-purple/20",children:"Process: AI Analysis"})}),s.jsx("div",{className:"mb-5 mx-auto w-full max-w-[200px] h-[100px] flex items-center",children:s.jsxs("div",{className:"rounded-lg bg-[#0d1117] border border-white/10 overflow-hidden shadow-lg w-full",children:[s.jsxs("div",{className:"flex items-center gap-1.5 px-3 py-2 bg-[#161b22] border-b border-white/10",children:[s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-red-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-yellow-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-green-500/70"})]}),s.jsxs("div",{className:"p-3 font-mono text-[10px] space-y-1.5",children:[s.jsx("div",{className:"text-white/40",children:"
"}),s.jsxs("div",{className:"relative inline-block ml-3",children:[s.jsx("span",{className:"text-octo-purple-light",children:""}),s.jsx("div",{className:"absolute -inset-1 border-2 border-octo-purple rounded animate-pulse"}),s.jsx("div",{className:"absolute -right-2 top-1/2 -translate-y-1/2 w-1.5 h-1.5 bg-octo-purple rounded-full animate-pulse"})]}),s.jsx("div",{className:"text-white/40",children:"
"})]})]})}),s.jsxs("div",{className:"mt-auto",children:[s.jsx("h3",{className:"text-xl font-bold mb-3 text-center",children:"2. Semantic Relocation"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed text-center",children:"Our AI finds the element that matches the original user intent using surrounding context, accessible roles, and text labels."})]})]}),s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm flex flex-col",children:[s.jsx("div",{className:"mb-4",children:s.jsx("span",{className:"inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-octo-teal/15 text-octo-teal border border-octo-teal/20",children:"Output: Code Patch"})}),s.jsx("div",{className:"mb-5 mx-auto w-full max-w-[200px] h-[100px] flex items-center",children:s.jsxs("div",{className:"rounded-lg bg-[#0d1117] border border-white/10 overflow-hidden shadow-lg w-full",children:[s.jsxs("div",{className:"flex items-center gap-1.5 px-3 py-2 bg-[#161b22] border-b border-white/10",children:[s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-red-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-yellow-500/70"}),s.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-green-500/70"})]}),s.jsxs("div",{className:"p-3 font-mono text-[10px] space-y-1",children:[s.jsx("div",{className:"text-white/70",children:"$ octomind pull"}),s.jsx("div",{className:"text-white/50",children:"> Syncing fixes..."}),s.jsx("div",{className:"text-green-400",children:"✔ Updated checkout.spec.ts"})]})]})}),s.jsxs("div",{className:"mt-auto",children:[s.jsx("h3",{className:"text-xl font-bold mb-3 text-center",children:"3. Source Update"}),s.jsxs("p",{className:"text-octo-text text-base leading-relaxed text-center",children:["We update the test definition in the cloud. You simply run"," ",s.jsx("code",{className:"text-octo-purple-light font-mono text-sm",children:"octomind pull"})," to apply the fix permanently to your local git branch."]})]})]})]})]})]})}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{className:"py-20 px-4 md:px-6",children:s.jsx("div",{className:"max-w-6xl mx-auto",children:s.jsxs("div",{className:"grid md:grid-cols-2 gap-12 md:gap-16 items-center",children:[s.jsxs("div",{children:[s.jsx("h2",{className:"text-3xl md:text-4xl font-black mb-6",children:"the fix lands in your repo"}),s.jsx("p",{className:"text-lg text-octo-text mb-8 leading-relaxed",children:"Runtime patching is temporary. Source healing is permanent. Octomind treats test maintenance as a code refactor. Review the fix in the cloud, then pull it to your repo."}),s.jsx("div",{className:"space-y-4",children:["Transparent Git Diff","Export as standard Playwright code","Full version history"].map((t,n)=>s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx("div",{className:"w-6 h-6 rounded-full bg-octo-teal/20 flex items-center justify-center flex-shrink-0",children:s.jsx(ve,{className:"w-4 h-4 text-octo-teal"})}),s.jsx("span",{className:"text-white font-medium",children:t})]},n))})]}),s.jsxs("div",{className:"rounded-xl overflow-hidden border border-white/10 bg-[#0d1117] shadow-2xl shadow-black/50",children:[s.jsxs("div",{className:"px-4 py-3 border-b border-white/10 bg-[#161b22] flex items-center justify-between",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-red-500/80"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-yellow-500/80"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-green-500/80"})]}),s.jsx("span",{className:"text-white/70 text-sm font-mono",children:"tests/checkout.yaml"})]}),s.jsx("span",{className:"px-2 py-0.5 rounded text-xs font-medium bg-yellow-500/20 text-yellow-400 border border-yellow-500/30",children:"Modified"})]}),s.jsxs("div",{className:"p-5 font-mono text-sm space-y-1",children:[s.jsxs("div",{className:"text-white/50 pl-4",children:[s.jsx("span",{className:"text-white/30 select-none mr-3",children:"4"}),'- name: Click "Complete Purchase"']}),s.jsxs("div",{className:"text-white/50 pl-4",children:[s.jsx("span",{className:"text-white/30 select-none mr-3",children:"5"}),"  action: click"]}),s.jsxs("div",{className:"bg-red-500/15 text-red-400 pl-4 -mx-5 px-5 py-0.5 border-l-2 border-red-500",children:[s.jsx("span",{className:"text-red-400/50 select-none mr-3",children:"6"}),s.jsx("span",{className:"text-red-400/70",children:"-"}),' selector: "#submit-btn-v1"']}),s.jsxs("div",{className:"bg-green-500/15 text-green-400 pl-4 -mx-5 px-5 py-0.5 border-l-2 border-green-500",children:[s.jsx("span",{className:"text-green-400/50 select-none mr-3",children:"6"}),s.jsx("span",{className:"text-green-400/70",children:"+"}),` selector: "[data-testid='submit-order']"`]})]})]})]})})}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{className:"py-20 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-14",children:[s.jsx("h2",{className:"text-3xl md:text-4xl font-black mb-4",children:"total control. zero surprises."}),s.jsx("p",{className:"text-lg text-octo-text max-w-2xl mx-auto",children:"No magic pushes. Maintain full authority over your codebase with human review and local verification."})]}),s.jsxs("div",{className:"grid md:grid-cols-3 gap-6",children:[s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm",children:[s.jsx("div",{className:"w-14 h-14 rounded-xl bg-gradient-to-br from-octo-purple/20 to-octo-teal/20 flex items-center justify-center mb-5",children:s.jsx(e6,{className:"w-7 h-7 text-octo-purple"})}),s.jsx("h3",{className:"text-xl font-bold mb-3",children:"Zero Silent Commits"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"Octomind never pushes code to your repository automatically. Every fix starts as a pending proposal waiting for your explicit approval."})]}),s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm",children:[s.jsx("div",{className:"w-14 h-14 rounded-xl bg-gradient-to-br from-octo-purple/20 to-octo-teal/20 flex items-center justify-center mb-5",children:s.jsx(a6,{className:"w-7 h-7 text-octo-teal"})}),s.jsx("h3",{className:"text-xl font-bold mb-3",children:"Human-in-the-Loop"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"You are the final gatekeeper. Review the proposed selector changes in the dashboard to ensure they align with your testing standards."})]}),s.jsxs("div",{className:"p-6 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm",children:[s.jsx("div",{className:"w-14 h-14 rounded-xl bg-gradient-to-br from-octo-purple/20 to-octo-teal/20 flex items-center justify-center mb-5",children:s.jsx(o6,{className:"w-7 h-7 text-octo-green"})}),s.jsx("h3",{className:"text-xl font-bold mb-3",children:"Verify Locally"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"Like it locally? Pull the proposed fix to your local dev environment and test it against your local app before merging."})]})]})]})}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{id:"faq",className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"mb-6 text-4xl md:text-5xl font-black text-white",children:"frequently asked questions"}),s.jsx("p",{className:"text-xl font-normal text-white",children:"Everything you need to know about Auto-Healing"})]}),s.jsx("div",{className:"space-y-4",children:[{question:'How is "Source-Level Healing" different from standard self-healing?',answer:'Standard tools use "runtime patching," which keeps the test green temporarily but leaves your code broken. Octomind performs Source-Level Healing, calculating a permanent fix for the Playwright selector and updating the test definition in your repository so your code stays healthy.'},{question:"Will Octomind change my code without permission?",answer:'Never. Octomind follows a "Zero Silent Commits" policy. We propose a fix when a test fails, but no code changes are applied until you explicitly review the proposal and run octomind pull to sync it to your branch.'},{question:"Does this work with Shadow DOM or Iframes?",answer:"Yes. Octomind's AI traverses the full DOM tree—including Shadow Roots and nested Iframes—to locate elements that standard selectors often miss. It works seamlessly with complex architectures and Web Components."},{question:"What happens if I stop using Octomind? (Vendor Lock-in)",answer:"You keep your tests. Octomind generates standard, open-source compatible test definitions. Since the healed selectors are committed directly to your repo, your test suite remains yours forever, with no dependency on our platform to run them."},{question:'Does it fix "flaky" tests or just broken ones?',answer:"Both. For broken selectors, we propose a code fix. For flaky tests caused by timing or network latency, Octomind applies internal stabilization logic (like smart auto-retries and dynamic waits) to ensure your tests pass reliably without requiring manual intervention."}].map((t,n)=>s.jsxs("details",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden hover:border-white/20 transition-all duration-300",children:[s.jsxs("summary",{className:"w-full px-6 md:px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden",children:[s.jsx("span",{className:"pr-8 text-sm font-normal text-white",children:t.question}),s.jsx("svg",{className:"w-6 h-6 text-octo-purple flex-shrink-0 transition-transform duration-300 group-open:rotate-180",strokeWidth:2,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),s.jsx("div",{className:"px-6 md:px-8 pb-6 leading-relaxed text-sm font-normal text-white",children:t.answer})]},n))})]})})]}),s.jsx(se,{})]})}const DD=["January","February","March","April","May","June","July","August","September","October","November","December"];function p_(e){const[t,n,r]=e.split("-"),o=Number(n)-1;if(!t||!n||!r||o<0||o>11)return e;const a=DD[o],i=String(Number(r));return`${a} ${i}, ${t}`}const RD=[{title:"openclaw in projects: real automation for people who'd rather be coding",excerpt:"OpenClaw is showing up in developer workflows as a self-hosted automation engine. Here's how teams actually use it — from CI/CD monitoring to expense management — and what we learned running it ourselves.",date:"2026-02-11",tags:["AI","automation","tools"],author:"Daniel Roedler",image:"/assets/blogs/openclaw-octoclaw.png",slug:"/blog/openclaw-in-projects-real-automation-for-people-whod-rather-be-coding"},{title:"Who's Actually Vibe Coding? The Data Doesn't Match the Hype",excerpt:"We scraped 500 people requesting a 'prompting bible' on LinkedIn. I expected broke founders building MVPs. The data told a completely different story.",date:"2026-01-28",tags:["AI","vibe coding","data"],author:"Daniel Roedler",image:"/assets/blogs/vibe-coding-data-expectation-reality.png",slug:"/blog/whos-actually-vibe-coding-the-data-doesnt-match-the-hype"},{title:"QA Agent in Your CI/CD Pipeline",excerpt:"We opened a PR. The QA agent flagged 12 edge cases we missed. It generated tests before code review even started.",date:"2026-01-22",tags:["testing","CI/CD","AI"],author:"Daniel Roedler",image:"/assets/blogs/qa-agent-cicd-pipeline.png",slug:"/blog/qa-agent-in-your-ci-cd-pipeline"},{title:"Where ChatGPT helps automate tests (and where it still falls short)",excerpt:"While ChatGPT can be immensely helpful, it’s not a full test automation engine. It shines in specific areas and falls short in others. Understanding that balance is key to using it well.",date:"2025-11-28",tags:["testing","LLMs"],author:"Analysis",image:"/assets/blogs/67f4f5386cca9ffdb78c14d8_mcp-topus.webp",slug:"/blog/where-chatgpt-really-helps-in-test-automation-and-where-it-still-falls-short"},{title:"11 enterprise-ready end-to-end testing tools (built for scale)",excerpt:"Large orgs need more than a cool demo. You need coverage across complex stacks, stable execution at scale, enterprise controls and low maintenance.",date:"2025-11-10",tags:["testing","enterprise"],author:"Analysis",image:"/assets/blogs/662b8701d21b9cddfa3778e1_6c1935a1228e4fb914fcfd7c02413bcc_Screenshot.webp",slug:"/blog/11-enterprise-ready-end-to-end-testing-tools-built-for-scale"},{title:"Why agents DO NOT write most of our code - a reality check",excerpt:`We love LLMs and use them everywhere we can, from our product to our internal workflows. But despite all the hype, we're nowhere near "agents writing most of our code."`,date:"2025-10-29",tags:["AI agents","coding"],author:"Veith Röthlingshöfer",image:"/assets/blogs/690206d18822df6c46d0ea02_ChatGPT_Image.webp",slug:"/blog/why-agents-do-not-write-most-of-our-code-a-reality-check"},{title:"Tests created with every PR",excerpt:"Create tests when committing code automatically - test batch generation of out of CI pipeline, test analytics and a SaaS designer talking. And month worth of shipped features from our Chief Product Officer.",date:"2025-10-23",tags:["product","features"],author:"Daniel Roedler",image:"/assets/blogs/66432d5823494fea94deef19_product_update.webp",slug:"/blog/product-update/25-10-23"},{title:"What is the Octomind design system?",excerpt:"Octomind is a software testing SaaS platform with fairly sophisticated UI. From the very beginning, we put effort into a good UX/UI. Design of the product was an inevitable part of it. This is how we've built our design system.",date:"2025-10-14",tags:["UX/UI","tools"],author:"Dániel Z. Aczél",image:"/assets/blogs/68ed072bb9580d45e4b40374_e89c8aac1ed6f4501d2b52a8c9998951_Screenshot.png",slug:"/blog/what-is-the-octomind-design-system"},{title:"Software testing stack guide: From unit tests to performance",excerpt:"This checklist lays out practical technology stacks for most common application types - SaaS, e-commerce, and canvas-based apps - and covers major testing categories from e2e to API testing.",date:"2025-10-13",tags:["testing","tools"],author:"Guide",image:"/assets/blogs/65df6ce7826a450727835ccf_ccc4e1c9c2a1c95f723212e4c6f9ea86_testing-pyramid.webp",slug:"/blog/software-testing-stack-guide-from-unit-tests-to-performance"},{title:"Don't get eaten by your devs",excerpt:"There's not much a developer hates more than a blocked pipeline by a flaky test. If the blocker is a real bug, no one argues - better to stop a bad merge than fire-drill it in production. But if the blocker turns out to be a flaky test?",date:"2025-10-01",tags:["testing","development","flakiness"],author:"Daniel Roedler",image:"/assets/blogs/68dd4550709326863d2da163_3f79e9b1dbba5cd0185f46e4852d3be4_ChatGPT.webp",slug:"/blog/organize-automated-tests-without-getting-eaten-by-your-devs"},{title:"9 agentic end-to-end testing tools to consider in 2025",excerpt:"We focused on tools that (1) generate or maintain tests with AI / agents, (2) cover real world testing use cases and features, (3) integrate with CI/CD, and (4) reduce flakiness and maintenance toil.",date:"2025-08-14",tags:["testing","AI"],author:"Analysis",image:"/assets/blogs/66192ec415dc72253efbb44a_single-tentacle.webp",slug:"/blog/9-agentic-end-to-end-testing-tools-to-consider-in-2025"},{title:"Why devs need AI-powered e2e test automation",excerpt:"Here are 6 reasons why we believe automated, AI-powered e2e tests belong to every development pipeline - for the benefit of both the code and the people who build it.",date:"2025-08-04",tags:["AI","testing","development"],author:"Daniel Roedler",image:"/assets/blogs/6890ac77d9b83aa9339b5c70_Screenshot.png",slug:"/blog/devs-need-e2e-tests"},{title:"Why we finally allowed arbitrary waits in our tests",excerpt:"We had a firm rule: no arbitrary sleeps in Octomind tests. Whenever someone asked, we pushed back. A hard-coded wait only papers over real bugs - and how do you even choose a number?",date:"2025-07-07",tags:["product","features"],author:"Kosta Welke",image:"/assets/blogs/686b897c7142a7a2515322b4_watch-octo.webp",slug:"/blog/why-we-finally-allowed-arbitrary-waits-in-our-tests"},{title:"Programmer yelling at the clouds about vibe coding",excerpt:"I see AI as a valuable tool, but only that. Something that can help you if used correctly and sanely, and can cause issues when letting it run rampant.",date:"2025-06-12",tags:["AI","codegen","testing"],author:"Daniel Draper",image:"/assets/blogs/684a915940a1c87b9d956d69_Screenshot.webp",slug:"/blog/programmer-yelling-at-the-clouds-about-vibe-coding"},{title:"AI testing: IDEs vs. testing platforms",excerpt:"AI in testing - how do IDEs compare with testing platforms? When are you better off with an agentic IDE scripting e2e tests and when would you use an AI testing platform?",date:"2025-05-22",tags:["testing","generative AI","LLMs"],author:"Daniel Roedler",image:"/assets/blogs/682e07f36bb7cb6640ba36fd_9a2511d3c0595f0eb2c92660b3e0f9f0_cursor-vs-octo.webp",slug:"/blog/cursor-vs-octomind"},{title:"My MCP vision for Octomind",excerpt:"Last week's open-sourcing of our MCP implementation is our contribution to the ecosystem. This week we are launching on Product Hunt. Let's turn testing from afterthought into a first-class citizen together.",date:"2025-04-02",tags:["AI agents","features"],author:"Marc Mengler",image:"/assets/blogs/67ecd85d43a7236461284086_vibe_testing_meme.jpg",slug:"/blog/my-mcp-vision-for-octomind"},{title:"Test isolation is (not) fun",excerpt:'Isolating context, tests, and data before execution makes running tests against multiple environments and parallelization possible. This is not a "best practice", but the way I see end-to-end test isolation.',date:"2025-03-11",tags:["testing","automation"],author:"Daniel Draper",image:"/assets/blogs/67d0323f047c90a6ab34c1b2_web-title-pic.webp",slug:"/blog/test-isolation-is-not-fun"},{title:"AI doesn't belong in test runtime",excerpt:"Not all AI in e2e testing is created equal. To deploy AI to testing successfully, you have to address concerns of brittleness, interpretability, and clunkiness. Mitigation strategies depend on the testing stage.",date:"2025-02-13",tags:["testing","generative AI","LLMs"],author:"Daniel Roedler",image:"/assets/blogs/67ae248b909c8d94caad5123_autofixDone.webp",slug:"/blog/ai-doesnt-belong-in-test-runtime"},{title:"Testing is more about setup than scripts",excerpt:"Test automation was never just the problem of creating scripts, but a challenge of proper setup and context. Autonomous testing tools should look at test automation as a whole and go beyond simple scripting to be useful.",date:"2025-01-31",tags:["testing","automation"],author:"Daniel Draper",image:"/assets/blogs/6799f7f2a14c6099642e6adf_9hs99q.jpg",slug:"/blog/testing-is-more-about-setup-than-scripts"},{title:"Stop automating manual test cases",excerpt:"There's more to test automation than simply creating scripts to automate test cases you would execute manually. Testing automation is highly analytical and requires a good knowledge of the system under test.",date:"2025-01-16",tags:["testing","automation"],author:"Veith Röthlingshöfer",image:"/assets/blogs/6787b46c51d6640a65c6baa8_automation-octo.webp",slug:"/blog/stop-automating-manual-test-cases"},{title:"How to start with e2e testing from scratch?",excerpt:"An Octomind tutorial for developers who want start end-to-end testing. Let me show you what getting up to speed with Octomind looks like so you can see if it's something you'd want to try out for your own websites or apps.",date:"2024-10-10",tags:["product","tutorial","testing"],author:"Daniel Roedler",image:"/assets/blogs/6706abc4c6ea64060bb750b6_Screenshot.webp",slug:"/blog/how-to-start-with-e2e-testing-from-scratch-an-octomind-tutorial"},{title:"Our problem with backlogs",excerpt:"A backlog is a collection of tasks that aren't important enough to do. If they were, we wouldn't be stashing them away. After years in software development I've come to see backlogs as more of a hindrance than a help.",date:"2024-08-12",tags:["software development"],author:"Daniel Draper",image:"/assets/blogs/66b63ba66735e61b1b241360_backlog-octo.webp",slug:"/blog/our-problem-with-backlogs"},{title:"Why we no longer use LangChain for our AI agents",excerpt:"When abstractions do more harm than good - lessons learned using LangChain in production and what we should've done instead.",date:"2024-06-13",tags:["agents","LLMs"],author:"Fabian Both",image:"/assets/blogs/666abdd1a2dd970e1452b269_DYI-octos.webp",slug:"/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents"},{title:"Did you break your code or is the test flaky?",excerpt:"Flaky e2e tests are frustrating for QA and development teams, causing constant disruptions and eroding trust in test outcomes. A fail-safe checklist for how you can tell the difference.",date:"2024-05-22",tags:["testing","tutorial"],author:"Maxi Link",image:"/assets/blogs/664cae51cf470851c5905e8b_snowflake-octo.webp",slug:"/blog/did-you-break-your-code-or-is-the-test-flaky"},{title:"We got funded!",excerpt:"We've raised $4.8 m to reinvent testing with AI. Led by Cherry Ventures, the round supports the developer tool's ambitions to create a bug-free future for AI-assisted software development and testing.",date:"2024-04-23",tags:["testing","investor news"],author:"Octoneers",image:"/assets/blogs/66152200e6cc761cf82573b7_A7309500.webp",slug:"/blog/octomind-raises-4-8-million-to-reinvent-software-testing-with-ai"},{title:"The full-stack testing mindset",excerpt:"When done right, end-to-end tests are invaluable tools for comprehensively testing a backend. Working e2e tests is in everyone's interest, whether you are on frontend, backend, engineering or QA.",date:"2024-03-28",tags:["testing","software development"],author:"Daniel Draper",image:"/assets/blogs/66053bc07722f3570e719067_coder-duo-clean.webp",slug:"/blog/the-full-stack-testing-mindset"},{title:"Why we built an e2e testing tool on top of Playwright",excerpt:"To build a better way of testing we needed a testing framework to complement our AI agents. Our ideal end-to-end testing tool would help build high quality apps fast.",date:"2024-03-07",tags:["testing","software development"],author:"Daniel Roedler",image:"/assets/blogs/65e7155616a5db17d34ab1ab_playwright-octopi.webp",slug:"/blog/why-we-built-an-e2e-testing-tool-on-top-of-playwright"},{title:"Keep your Copilot and your code quality",excerpt:"How do we get quality back on track while keeping the productivity benefits of tools like Copilot? After all, degrading quality undermines delivery speed and neutralizes some of the gains, if not most.",date:"2024-02-12",tags:["generative AI","AI coding assistants"],author:"Daniel Roedler",image:"/assets/blogs/65c90563ef0c29c770b57ee1_saving-octo.webp",slug:"/blog/keep-your-copilot-and-your-code-quality-with-ai-testing"},{title:"We went viral with a broken app",excerpt:"Post mortem on a weekend of missed opportunity. What we've learned on moving fast and making sure we don't break things thanks to an unexpectedly viral blog posted to a subreddit on a Friday afternoon.",date:"2023-12-15",tags:["software development","testing"],author:"Octoneers",image:"/assets/blogs/657b614e36bb1b5c79216628_burning-octo-1200.png",slug:"/blog/we-went-viral-with-a-broken-app"},{title:"Test your code!",excerpt:"Software testing is littered with strong opinions and i'm no different. What, when, who, how and if to test at all is the subject of heated discussions. About the one dogma I'm guilty of - testing what you've built.",date:"2023-12-05",tags:["software development","testing"],author:"Daniel Draper",image:"/assets/blogs/656f2d9457d5c3ccb9f8aa2b_preacher-octo-1200.jpg",slug:"/blog/test-your-code"},{title:"Never Ship on Fridays",excerpt:"Deploying on Fridays is a legend of programming memes - a practice only fools engage in. Being completely averse to Friday releases can come across as dogmatic. My take #3 on popular developer dogmas.",date:"2023-11-08",tags:["software development"],author:"Daniel Draper",image:"/assets/blogs/654b9dccfc95e0be12246fcb_sleepy-octo-1200.png",slug:"/blog/on-developer-dogma-3-never-ship-on-fridays"},{title:"Navigating TypeScript Gymnastics",excerpt:"An attempt for reconciliation between TypeScript lovers and haters. I'm firmly in the TypeScript camp. However, using typescript is also not an absolute.",date:"2023-11-07",tags:["software development"],author:"Daniel Draper",image:"/assets/blogs/654b87eb5e250bee25343564_gym-octo-1200.png",slug:"/blog/navigating-the-typescript-gymnastics-on-developer-dogma-2"},{title:"Deconstructing Popular Developer Dogmas",excerpt:"Opinions in software engineering have gotten more extreme lately. I'll use some notorious examples to show that nuance is an essential engineering principle.",date:"2023-11-06",tags:["software development"],author:"Daniel Draper",image:"/assets/blogs/65118a7e3ff7f9c54bc2609e_laptop.png",slug:"/blog/deconstructing-popular-developer-dogmas-1"},{title:"On Type Safety in LangChain TS",excerpt:"The unpredictable and non-deterministic nature of LLM output makes ensuring type safety hard. Lessons I learned about parsing and error handling of LangChain.",date:"2023-10-04",tags:["LLMs","type safety"],author:"Veith Röthlingshöfer",image:"/assets/blogs/651de533a7889925905aa5d9_LLMs-nutshell-small.png",slug:"/blog/on-type-safety-in-langchain-ts"},{title:"Testing Pyramid: An Evolutionary Tale",excerpt:"Change is ubiquitous in software development. New languages, tools and frameworks are everywhere. Yet the one thing unwilling to move is the Testing Pyramid.",date:"2023-10-02",tags:["software testing","e2e tests"],author:"Marc Mengler",image:"/assets/blogs/651de52708dde83fd8103488_2pyramids-small.png",slug:"/blog/testing-pyramid-an-evolutionary-tale"}];function MD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("section",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto text-center relative z-10",children:s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"mb-6 text-5xl md:text-6xl font-black text-white",children:"blogtopus"}),s.jsx("p",{className:"max-w-2xl mx-auto text-xl font-normal text-octo-text",children:"Insights, tutorials, and best practices for modern software testing"})]})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto relative z-10",children:s.jsx("div",{className:"grid md:grid-cols-2 lg:grid-cols-3 gap-8",children:RD.map((e,t)=>s.jsx("article",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl hover:border-white/20 transition-all cursor-pointer group animate-fade-in-up",children:s.jsxs(Fs,{to:e.slug,children:[e.image&&s.jsx("div",{className:"w-full h-48 overflow-hidden rounded-t-2xl",children:s.jsx("img",{src:e.image,alt:e.title,className:"w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"})}),s.jsxs("div",{className:"p-6",children:[s.jsx("div",{className:"flex items-center gap-3 mb-4 flex-wrap h-8",children:e.tags?e.tags.map((n,r)=>s.jsx("span",{className:"px-3 py-1 bg-[#5c5bdb]/10 border border-[#5c5bdb]/20 rounded-full",style:{fontSize:"12px",fontWeight:400,color:"#5c5bdb"},children:n},r)):""}),s.jsx("h2",{className:"mb-3 text-xl font-bold text-white transition-colors",children:e.title}),s.jsx("p",{className:"mb-4 text-sm font-normal text-octo-text leading-relaxed",children:e.excerpt}),s.jsx("time",{className:"text-xs font-normal text-octo-text",children:p_(e.date)}),e.author&&s.jsx("p",{style:{fontSize:"12px",fontWeight:400,color:"#C3C9D3"},children:e.author})]})]})},t))})})}),s.jsx(se,{})]})}const $D=[{title:"Leaping AI ships faster with reliable e2e tests - without hiring a QA",excerpt:"Leaping AI builds self-improving voice AI agents for phone support. But while their product evolves autonomously, their internal QA did not. Until Octomind came to help.",date:"2025-12-03",tags:["product","real world use"],author:"Case Study",image:"/assets/blogs/6930188590f133d5aa86a664_youtube-thumb.jpg",slug:"/case-studies/how-leaping-ai-ships-faster-with-reliable-e2e-tests-without-hiring-a-qa"},{title:"Scaling QA to match the speed of engineering innovation",excerpt:"How Deriv, one of the world's largest online brokers, transformed testing with Octomind to build a scalable, resilient QA framework to align with modern development cycles.",date:"2025-08-07",tags:["product","real world use"],author:"Case study",image:"/assets/blogs/68b01a9b0b1a35cdf22b42b9_81a25983d418568b78f839bf628e0aba_youtube-thumb.webp",slug:"/case-studies/scaling-qa-to-match-the-speed-of-engineering-innovation"},{title:"Faster QA for early movers in AI sales coaching",excerpt:"Octomind automated QA so WingRep could scale the development of their sales coaching app without the risk of buggy experience.",date:"2025-07-16",tags:["product","real world use"],author:"Case Study",image:"/assets/blogs/68e3c8d26a2be6dcd3d8774c_IMG8504.webp",slug:"/case-studies/how-to-deliver-sales-coaching-quality-without-slowing-down"},{title:"Big SaaS cuts test maintenance by 83% with Octomind auto-fix",excerpt:"Enterprise platform SaaS solved test maintenance thanks to Octomind advanced debugging and auto-fix features.",date:"2025-05-15",tags:["product","real world use"],author:"Case Study",image:"/assets/blogs/689f25488a25ccda5683eb98_Group_142.webp",slug:"/case-studies/big-saas-cut-test-maintenance-by-83-percent"},{title:"Big SaaS cut $300K in QA costs & quadrupled automation",excerpt:"High volume test automation of a large app - a rapidly scaling B2B SaaS company was stuck in a costly and slow testing cycle struggling to keep pace with twice-weekly releases.",date:"2025-04-12",tags:["product","real world use"],author:"Case study",image:"/assets/blogs/689b55eb8a0983401e705b00_7ebc581facfbb59f5890bd5a13de9585_Group_141.png",slug:"/case-studies/big-saas-automation"}];function LD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("section",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto text-center relative z-10",children:s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"mb-6 text-5xl md:text-6xl font-black text-white",children:"octomind used in the real world"}),s.jsx("p",{className:"max-w-2xl mx-auto text-xl font-normal text-octo-text",children:"Projects, customer testimonials and use cases of Octomind tests deployed in the wild. See examples of ROI on using AI-powered end-to-end testing."})]})})}),s.jsx("section",{className:"py-16 px-4 md:px-6 relative",children:s.jsx("div",{className:"max-w-6xl mx-auto relative z-10",children:s.jsx("div",{className:"grid md:grid-cols-2 lg:grid-cols-3 gap-8",children:$D.map((e,t)=>s.jsx("article",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl hover:border-white/20 transition-all cursor-pointer group animate-fade-in-up",children:s.jsxs(Fs,{to:e.slug,children:[e.image&&s.jsx("div",{className:"w-full h-48 overflow-hidden rounded-t-2xl",children:s.jsx("img",{src:e.image,alt:e.title,className:"w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"})}),s.jsxs("div",{className:"p-6",children:[s.jsx("div",{className:"flex items-center gap-3 mb-4 flex-wrap h-8",children:e.tags.map((n,r)=>s.jsx("span",{className:"px-3 py-1 bg-[#5c5bdb]/10 border border-[#5c5bdb]/20 rounded-full",style:{fontSize:"12px",fontWeight:400,color:"#5c5bdb"},children:n},r))}),s.jsx("h2",{className:"mb-3 text-xl font-bold text-white transition-colors",children:e.title}),s.jsx("p",{className:"mb-4 text-sm font-normal text-octo-text leading-relaxed",children:e.excerpt}),s.jsx("time",{className:"text-xs font-normal text-octo-text",children:p_(e.date)}),e.author&&s.jsx("p",{style:{fontSize:"12px",fontWeight:400,color:"#C3C9D3"},children:e.author})]})]})},t))})})}),s.jsx(se,{})]})}function FD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("header",{className:"bg-octo-dark border-b border-white/10 relative z-10 pt-24",children:s.jsx("div",{className:"max-w-4xl mx-auto px-4 md:px-6 py-6",children:s.jsx("div",{className:"animate-fade-in-up",children:s.jsx("h1",{className:"text-5xl md:text-6xl font-black text-white",children:"Data Privacy and Security"})})})}),s.jsx("main",{className:"max-w-4xl mx-auto px-4 md:px-6 py-16 md:py-24 relative z-10",children:s.jsx("div",{className:"animate-fade-in-up",children:s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-8 md:p-12 space-y-8",children:[s.jsx("section",{children:s.jsx("p",{className:"leading-relaxed text-sm text-white",children:"At Octomind, we take data privacy and security seriously and follow best practices to ensure that our customers' data is handled responsibly. We only collect and process data as necessary to deliver our core services, such as website testing, and we do not share any data with third parties outside of essential service providers like infrastructure providers."})}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Data Retention Policy"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["We ensure that ",s.jsx("strong",{className:"text-white",children:"all user data related to tests is deleted after 6 weeks"}),", with the exception of essential user profiles that facilitate service continuity."]}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text mt-4",children:["All data is processed solely for the intended purpose of delivering and improving our services, and we strive to uphold the principles of ",s.jsx("strong",{className:"text-white",children:"data minimization, purpose limitation, and security."})," To ensure clients' needs are fully met, we are open to discussing any specific privacy or security concerns and are happy to sign a mutual NDA if needed."]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"User Data Protection"}),s.jsxs("div",{className:"space-y-4",children:[s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"No unnecessary data collection:"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"We do not collect or store any customer data beyond what is necessary for test case creation and execution."})]}),s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Data processing:"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"HTML and screenshots are analyzed during the test creation process. HTML is never stored, and screenshots are kept as part of the test report, following our data retention policy."})]}),s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Data retention:"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"Test runs generate traces and screenshots, which are used solely in test reports to help users understand the quality of the system. All artifacts (such as traces and screenshots) are automatically deleted after six weeks as part of our standard deletion policy."})]}),s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Security:"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"Industry-standard security measures, such as encryption and secure access protocols, are implemented to ensure data confidentiality and protection."})]})]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"No Training and Sharing with Third Parties"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"Our solution only uses input data for the purpose of composing, running, and maintaining test cases. We do not use this data to train our solution, nor do we share input or output data with third-party companies outside of our standard service providers, such as AWS, Azure or GCP, which are necessary for infrastructure purposes."})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Human Inspection"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"Human inspections of input or output data are not systematically conducted unless explicitly requested for support purposes. In such cases, only authorized support personnel have access, solely for the specific purpose of resolving issues. We analyze system failures, especially if our AI agent encounters difficulties during test case creation. These inspections are limited to individual test cases or specific steps within test cases and are carried out only by authorized support personnel."})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Generated Code: Access and Ownership"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"Output data (such as Playwright code) generated by our solution is not subject to third-party rights, nor are there limitations on your use of the generated code. Clients have full access to the Playwright code, which they can use as needed."}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text mt-4",children:'The source code generated by Octomind is not available in any public repositories and is not subject to external licensing. Our solution generates an intermediate representation (a data structure describing user flows, such as "click on T-shirt," "select size M," etc.), which is then deterministically converted into Playwright code without the use of AI. The generated Playwright code is fully owned by the client, with no external license requirements.'})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Use of PostHog Analytics"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["To improve user experience and service quality, we have introduced the use of the PostHog application to track user behaviour on our website and the app. This tool assists us in understanding how users interact with our website, and helps us make improvements based on these insights. The anonymized information that we gather through PostHog is stored securely in the EU cloud instance of PostHog. This ensures that all your data is managed in compliance with the strict data protection laws of the European Union, offering you robust security and privacy. Read more about PostHog data policy at"," ",s.jsx("a",{href:"https://posthog.com/privacy",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"posthog.com/privacy"}),"."]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Octomind's Cookie Policy"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"This Cookie Policy is an integral part of Octomind's Privacy Policy. It explains how we, at Octomind, use cookies on our website (https://www.octomind.dev) and our app (https://app.octomind.dev). Cookies are vital for enhancing your online experience with us."}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text mt-4",children:"It's important to note that this policy does not encompass the cookies used by these third parties. Should you prefer not to accept cookies from Octomind, you can set your browser to reject cookies other than essential cookies. However, this may result in a limited experience on our site, as some services and content might become unavailable."}),s.jsx("h3",{className:"mt-6 mb-3 text-lg text-white font-medium",children:"Understanding Cookies"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["A cookie is a tiny data file stored on your device by a website you visit. It often includes details about the site, a unique identifier to recognize your browser on subsequent visits, the cookie's purpose, and its duration. Cookies have several uses - they enable website features, track site usage for analytics, remember your settings (like time zone and preferences), and personalize your experience (like showing relevant ads and language settings). First-party cookies are set by the site you're visiting, tracking only your activity on that site. Third-party cookies, however, are set by external sites and companies and might track your activity across multiple websites using the same service."," ",s.jsx("strong",{className:"text-white",children:"Octomind uses first-party cookies only."})]}),s.jsx("h3",{className:"mt-6 mb-3 text-lg text-white font-medium",children:"Types of Cookies and Their Usage at Octomind"}),s.jsxs("div",{className:"space-y-4 mt-4",children:[s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Essential Cookies:"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["These are vital for website functionality like account management and payment processes. Octomind uses essential cookies for authentication services to provide a secure log in to our app. We use"," ",s.jsx("a",{href:"https://www.propelauth.com/",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"PropelAuth authentication"})," ","for these purposes."]})]}),s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Performance Cookies:"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["These cookies collect data about your website usage, helping us understand user behavior, pinpoint problems, and enhance overall user experience. This data is usually anonymous. Octomind employs performance cookies for these insights. We use"," ",s.jsx("a",{href:"https://posthog.com/",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"PostHog analytics"})," ","for these purposes."]})]}),s.jsxs("div",{children:[s.jsx("p",{className:"mb-2 text-sm text-white font-medium",children:"Targeting/Advertising Cookies:"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["Used for tailoring relevant promotional content to your interests, these cookies aid in delivering targeted ads and controlling ad frequency. ",s.jsx("strong",{className:"text-white",children:"Octomind does not utilize targeting/advertising cookies."})]})]})]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"What Rights Do You Have Regarding Your Data?"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"You always have the right to request information about your stored data, its origin, its recipients, and the purpose of its collection at no charge. You also have the right to request that it be corrected, blocked, or deleted. You can contact us at any time using the address given in the legal notice if you have further questions about the issue of privacy and data protection. You may also, of course, file a complaint with the competent regulatory authorities."})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white font-semibold",children:"Contact Us"}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"If you have any questions about this Privacy Policy, please contact us at:"}),s.jsx("div",{className:"mt-4 p-4 bg-octo-card/60 rounded-lg",children:s.jsx("p",{className:"text-sm text-white",children:s.jsx("a",{href:"mailto:info@octomind.dev",className:"text-octo-purple hover:text-octo-purple/80",children:"info@octomind.dev"})})})]})]})})}),s.jsx(se,{})]})}function zD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("header",{className:"bg-octo-dark border-b border-white/10 relative z-10 pt-24",children:s.jsx("div",{className:"max-w-4xl mx-auto px-4 md:px-6 py-6",children:s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"text-5xl md:text-6xl font-black text-white",children:"Legal / Impressum"}),s.jsx("p",{className:"text-sm text-octo-text mt-2",children:"Information in accordance with section 5 TMG"})]})})}),s.jsx("main",{className:"max-w-4xl mx-auto px-4 md:px-6 py-16 md:py-24 relative z-10",children:s.jsx("div",{className:"animate-fade-in-up",children:s.jsxs("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-8 md:p-12 space-y-8",children:[s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white",children:"Company Information"}),s.jsxs("div",{className:"space-y-3 text-sm text-octo-text",children:[s.jsxs("p",{children:[s.jsx("span",{className:"text-white",children:"Company Name:"})," OctoMind GmbH"]}),s.jsxs("p",{children:[s.jsx("span",{className:"text-white",children:"Address:"})," Unterer Lussweg 5, 76227 Karlsruhe, Germany"]}),s.jsxs("p",{children:[s.jsx("span",{className:"text-white",children:"Email:"})," ",s.jsx("a",{href:"mailto:info@octomind.dev",className:"text-octo-purple hover:text-octo-purple/80",children:"info@octomind.dev"})]}),s.jsxs("p",{children:[s.jsx("span",{className:"text-white",children:"Represented by:"})," Daniel Roedler, Marc Mengler"]}),s.jsxs("p",{children:[s.jsx("span",{className:"text-white",children:"Registration:"}),' Registered in Mannheim district court ("Amtsgericht Mannheim") under HRB 747257']})]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white",children:"Terms and Conditions"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["Find terms and conditions of OctoMind GmbH"," ",s.jsx("a",{href:"https://assets.octomind.dev/General_Terms_and_Conditions_AGB_Octomind_English.pdf",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"here"}),"."]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white",children:"Open-source Licenses"}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["Find the open-source licenses we use on"," ",s.jsx("a",{href:"https://assets.octomind.dev/licenses.json",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"this link (json file)"}),"."]})]}),s.jsxs("section",{children:[s.jsx("h2",{className:"mb-4 text-xl text-white",children:"Disclaimers"}),s.jsxs("div",{className:"space-y-6",children:[s.jsxs("div",{children:[s.jsx("h3",{className:"mb-3 text-sm text-white",children:"Website Content"}),s.jsx("p",{className:"leading-relaxed mb-3 text-sm text-octo-text",children:"The content of our website is managed by OctoMind GmbH. We take great care to ensure the accuracy and reliability of the information on our website."}),s.jsx("p",{className:"leading-relaxed text-sm text-octo-text",children:"However, we cannot be held responsible for any errors or omissions, or for any actions taken based on the information provided on this website. Our website may contain links to external websites or resources that are not under our control. We do not endorse or accept responsibility for the content or use of any external website or resource. Should any legal infringement become known to us, we will remove the respective link immediately."})]}),s.jsxs("div",{children:[s.jsx("h3",{className:"mb-3 text-sm text-white",children:"Copyright"}),s.jsx("p",{className:"leading-relaxed mb-3 text-sm text-octo-text",children:"All content on this website, including text, images, and videos, is subject to copyright protection. OctoMind GmbH's web pages and their contents are subject to German copyright law. Unless expressly permitted by law (§ 44a et seq. of German copyright law), every form of utilizing, reproducing or processing works subject to copyright protection on our website requires the prior consent of the respective owner of the rights."}),s.jsx("p",{className:"leading-relaxed mb-3 text-sm text-octo-text",children:"Individual reproductions of a work are allowed only for private use, so must not serve either directly or indirectly for earnings. Unauthorized utilization of copyrighted works is punishable (§ 106 of German copyright law)."}),s.jsxs("p",{className:"leading-relaxed text-sm text-octo-text",children:["The fonts used on this website are provided by Adobe Fonts. Further information on data protection and the use of Adobe Fonts can be found in the privacy policy of Adobe Systems Software Ireland Limited at"," ",s.jsx("a",{href:"https://adobe.com/privacy/policies/adobe-fonts.html",target:"_blank",rel:"noopener noreferrer",className:"text-octo-purple hover:text-octo-purple/80 underline",children:"Adobe.com"}),"."]})]})]})]})]})})}),s.jsx(se,{})]})}const BD=()=>s.jsx(ap,{to:"/",replace:!0});function ge(){return s.jsx("div",{className:"mt-16 text-center",children:s.jsx(Fs,{to:"/blog",children:s.jsx(ut,{variant:"outline",className:"border-octo-purple text-octo-purple hover:bg-octo-purple hover:text-white transition-all",children:"read more blogtoposts"})})})}function de({to:e="/blog",label:t="Back to blog"}){return s.jsxs(Fs,{to:e,className:"inline-flex items-center text-muted-foreground hover:text-primary mb-8 transition-colors",children:[s.jsx(q4,{className:"w-4 h-4 mr-2"}),t]})}function WD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"where ChatGPT really helps in test automation (and where it falls short)"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Helpful yet no full test automation engine"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"AI is becoming part of almost every software development workflow, and test automation is no exception. ChatGPT in particular has quickly become a go-to companion for testers and developers. It helps teams move faster, understand requirements more clearly, and produce test code with far less friction."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"But while ChatGPT can be immensely helpful, it's not a full test automation engine. It shines in specific areas and falls short in others. Understanding that balance is key to using it well."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"How to use ChatGPT for test automation"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Many teams start using ChatGPT because it removes a lot of the grunt work from scripting tests in particular. Instead of writing every test case or test script by hand, you can have ChatGPT draft the foundation - leaving you to refine and adapt it."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Here are the areas where ChatGPT consistently performs well:"}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"1. Turning requirements into test ideas"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Give it a user story, acceptance criteria, or a product description, and it can outline:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"core functional tests"}),s.jsx("li",{children:"edge cases"}),s.jsx("li",{children:"negative paths"}),s.jsx("li",{children:"acceptance tests"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"It often surfaces scenarios teams forget when they're moving fast."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"2. Generating or refactoring test code"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"ChatGPT is strong at producing boilerplate test code in frameworks like:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"Playwright"}),s.jsx("li",{children:"Cypress"}),s.jsx("li",{children:"Selenium"}),s.jsx("li",{children:"REST Assured"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"It can also translate tests between frameworks, clean up messy code, and help you choose better patterns."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"3. Debugging failing tests"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Paste a failing stack trace or logs and ask what might be wrong. ChatGPT can spot patterns or incorrect assumptions quickly and suggest the most likely fixes."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"4. Creating test data"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Need complex JSON payloads, test personas, or malformed inputs? ChatGPT can generate them instantly, saving you from manual formatting."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"5. Supporting exploratory testing"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:`It's surprisingly good at brainstorming "how this feature might break," helping you widen your exploratory coverage.`}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Where ChatGPT still falls short"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:["Even though ChatGPT feels powerful, it's important to remember what it ",s.jsx("em",{children:"can't"})," do."]}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"It doesn't interact with your actual application"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"ChatGPT cannot:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"open a browser"}),s.jsx("li",{children:"run tests"}),s.jsx("li",{children:"validate UI changes"}),s.jsx("li",{children:"observe how your product behaves in real time"}),s.jsx("li",{children:"manage variables"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"It only reasons from text."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"It doesn't interact with other applications"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It can't do:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"email testing"}),s.jsx("li",{children:"2-factor authentication"}),s.jsx("li",{children:"Salesforce (et al.) testing"})]}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"Generated tests may not run as-is"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Because it doesn't execute the code:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"selectors may be wrong"}),s.jsx("li",{children:"APIs may not match your backend"}),s.jsx("li",{children:"async logic may be off"}),s.jsx("li",{children:"configs may be missing"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Human review is always required."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"It can't maintain tests automatically"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"ChatGPT will not update your selectors when the UI changes or proactively repair failing tests. You must feed it the updated context each time. It can't maintain the test code."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"It has no native understanding of your system"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Unless you paste everything in, it doesn't know:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"your architecture"}),s.jsx("li",{children:"environment settings"}),s.jsx("li",{children:"real staging data"}),s.jsx("li",{children:"your team's naming conventions"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"This limits how accurate it can be."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"It can't deal with a growing test suite without any context engineering"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Full-stack testing platforms will never be overloaded by a 10,000 LoC test suite, but ChatGPT will, because it doesn't need to/can't see context by definition."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"Security and privacy considerations"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Not every organization wants to paste logs, source code, or crash reports into a public AI."}),s.jsxs("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:["If you want AI to truly ",s.jsx("span",{style:{color:"#15D7AB",fontStyle:"italic"},children:"automate"})," testing"]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"ChatGPT is fantastic as an assistant - but test automation requires executing tests, inspecting the UI, and adapting to change. For that, you need tools built specifically for autonomous or AI-supported testing."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"If your goal is AI-driven test creation, execution, and self-healing, consider platforms such as:"}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"AI-powered E2E testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"Octomind"}),s.jsx("li",{children:"Testsigma"}),s.jsx("li",{children:"Mabl"}),s.jsx("li",{children:"Spur"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"These tools can interact with your product, generate tests from real UI flows, and automatically repair broken selectors."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"AI-powered visual regression"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"Applitools Eyes"}),s.jsx("li",{children:"Percy"}),s.jsx("li",{children:"Meticulous"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Great for detecting UI changes that would be tedious to capture with assertions."}),s.jsx("h3",{className:"text-xl font-bold text-octo-purple-light mt-8 mb-3",children:"AI for API and backend testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"Postman with AI features"}),s.jsx("li",{children:"Keploy"}),s.jsx("li",{children:"ReadyAPI"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Helpful for generating tests based on live traffic or API specifications."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"These tools go beyond assistance — they act on your application, maintain tests, and integrate deeply with CI/CD."}),s.jsx("h2",{className:"text-3xl font-black text-white mt-12 mb-4",children:"The bottom line"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"ChatGPT has become an invaluable helper for testers. It speeds up thinking, writing, debugging, and brainstorming. But it doesn't replace test automation frameworks, nor does it maintain tests or adapt them to product changes."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"In a modern testing setup, ChatGPT is best used alongside specialized AI-powered testing platforms. ChatGPT accelerates understanding and creation; those tools handle execution and self-healing."}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function UD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"11 end-to-end testing tools built for scale"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-4",children:"Why this list"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Large orgs need more than a cool demo. You need coverage across complex stacks, stable execution at scale, enterprise controls (security, governance, SSO), and low maintenance. The tools below are proven at that layer - either full testing platforms or the execution infrastructure your test suites run on. We've tried to summarize a few testing platforms that address the enterprise space."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"1) Octomind"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Agent-powered, fully managed Playwright tests, all-in-one platform for test generation to scale-out execution and automated maintenance"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"AI agent generates Playwright tests from your app or natural language prompts, analyzes and auto-heals broken tests"}),s.jsx("li",{children:"Managed, scalable cloud runner"}),s.jsx("li",{children:"CI/CD, MCP, CLI, test management integrations"}),s.jsx("li",{children:"Advanced test run analysis with automated failure identification and test auto-fix"}),s.jsx("li",{children:"Works on private apps and multiple environments"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Product teams that want enterprise-grade E2E coverage without staffing a big QA."]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Fast ramp up - working large test suite in days"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Automated maintenance"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Offloads testing infrastructure"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Easy-to-use UI for testers with no coding skills, while offering full range of dev functionality"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Cloud-first model; highly bespoke/on-prem constraints may need discussion"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Best fit if you accept Playwright testing code as the execution standard"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"2) Playwright (Microsoft)"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Modern open-source E2E framework with built-in parallel runner, auto-waits, and a powerful trace viewer"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"First-class parallelism and sharding"}),s.jsx("li",{children:"Auto-waiting / actionability checks reduce flake"}),s.jsx("li",{children:"Codegen test recorder"}),s.jsx("li",{children:"Rich trace viewer for debugging"}),s.jsx("li",{children:"Headless / headed and UI mode"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Engineering orgs standardizing on code-first testing with modern tooling"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Enterprise-friendly stability and debuggability"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Great DX, broad community support"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Open source"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," You still own test infra and have to manage scaling yourself"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," No built-in device cloud (use Sauce/ BrowserStack/ LambdaTest)"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"3) Cypress"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Popular E2E testing framework, with a hosted cloud runner for CI insights, parallelization, and test replay"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Browser-in-the-loop runner and dev-friendly debugging"}),s.jsx("li",{children:"Dashboard with analytics, flake detection, and parallelization"}),s.jsx("li",{children:"CI integrations and test replay"}),s.jsx("li",{children:"Automated accessibility testing"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Frontend-heavy dev teams that want strong local DX plus managed CI analytics."]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Fast authoring and debugging feedback loops"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Mature Cloud offering for scaling teams"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Large community for support"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Historically opinionated browser model; multi-window/ iframe edge cases need care"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Feature depth tied to Cloud plans for larger orgs"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"4) Selenium"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," A mature test automation framework, Selenium WebDriver standard that underpins most large-scale, cross-browser test automation"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"W3C WebDriver protocol; broad browser support"}),s.jsx("li",{children:"Language bindings across Java, Python, JS, and more"}),s.jsx("li",{children:"Selenium Grid for distributed execution"}),s.jsx("li",{children:"Massive ecosystem and vendor support"}),s.jsx("li",{children:"Extensible to mobile via Appium"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Enterprises needing standards-based coverage across diverse stacks and legacy browsers"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Universally supported, huge community"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Works with every major device cloud and infrastructure provider"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Open source"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," An older foundation, the tech often not state-of-the-art"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Test operations / maintainability can be non-trivial at scale"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"5) WebdriverIO"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Open-source browser and mobile automation test framework for Node.js with rich services (DevTools, Appium) and plugins"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Unified API over WebDriver and Chrome DevTools"}),s.jsx("li",{children:"First-class Appium integration for mobile"}),s.jsx("li",{children:"Flexible runner, reporters, and service ecosystem"}),s.jsx("li",{children:"Works with major clouds (Sauce, BrowserStack, LambdaTest)"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," JS/TS shops that want framework ergonomics on top of WebDriver with mobile reach."]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Extensible services model; strong community"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Covers web + mobile with one toolchain"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Node-centric; mixed-language orgs may standardize elsewhere"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," DevTools mode limited to Chromium family"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"6) Tricentis Tosca"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Model-based, codeless automation built for complex enterprise apps (ERP, mainframe, SAP)"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Model-based test design (high reuse, lower maintenance)"}),s.jsx("li",{children:"Vision AI for hard-to-automate UIs and remote desktops"}),s.jsx("li",{children:"Risk-based test optimization"}),s.jsx("li",{children:"API simulation and test data management"}),s.jsx("li",{children:"Scalable, distributed execution"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Enterprises with heterogeneous, packaged, and legacy systems needing non-code authoring at scale"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Strong coverage for SAP/ERP/mainframe landscapes"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Reduces script churn in fast-changing UIs"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Complementary tool ecosystem under the Tricentis umbrella (e.g. Testim)"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Licensing / enablement effort is significant"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Less flexible for teams preferring code-first patterns"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"7) Katalon"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Low code test automation with enterprise platform for test planning, execution and analytics"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Web, mobile, API, and desktop coverage in one suite"}),s.jsx("li",{children:"AI assistance for test creation and test healing"}),s.jsx("li",{children:"Advanced reporting"}),s.jsx("li",{children:"Extensive test execution functionality"}),s.jsx("li",{children:"Reporting, dashboards, and governance controls"}),s.jsx("li",{children:"Marketplace and CI integrations"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Orgs mixing SDET code and no-code testers, needing governance plus easier onboarding"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Low-code, full-code and AI scripting options"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Agentic test generation based on AI generated journeys"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Versatile test runner"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Advanced customization may hit platform boundaries"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Enterprise features sit behind highest, quote based tier"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"8) Mabl"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Low-code-first web E2E testing platform with AI assist, with a broad range of testing incl. accessibility and performance testing"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Low-code test creation with AI assistance"}),s.jsx("li",{children:"Jira, Slack, MS Teams integrations"}),s.jsx("li",{children:"Automated regression testing"}),s.jsx("li",{children:"Reporting and failure insights"}),s.jsx("li",{children:"Enterprise trust center & certifications"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Product orgs wanting a hosted platform to broaden coverage without heavy framework work"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Reduced maintenance vs. raw scripts"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Email and PDF testing"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Strong cloud runner"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Less control than code-first frameworks"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Complex edge cases can still need custom code"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"9) Sauce Labs"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Large enterprise platform with compliance focus and unified web and mobile testing"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Big real / virtual device and browser catalog"}),s.jsx("li",{children:"Parallel cross-browser runs and analytics"}),s.jsx("li",{children:"Private and public cloud options"}),s.jsx("li",{children:"Enterprise security documentation and certifications"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Enterprises that need to scale execution and device coverage globally"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Offloads grid/device lab entirely"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Realtime analytics"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Mature enterprise posture (security, support)"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Usage costs can rise with test volume"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Enterprise features sit behind highest, quote based tier"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"10) BrowserStack"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Enterprise-grade cross-browser/device cloud automation for web, mobile, visual and accessibility testing"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"AI agents for self-healing, failure analysis, and low code authoring"}),s.jsx("li",{children:"Real device cloud and desktop browsers"}),s.jsx("li",{children:"Enterprise SSO, access controls, data controls"}),s.jsx("li",{children:"Global regions and performance-tuned execution"}),s.jsx("li",{children:"Security & compliance enterprise features"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Teams standardizing on a hosted execution cloud with enterprise compliance requirements."]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Broad tool/framework support"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Proven scale and reliability"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Visual and accessibility testing"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Cost scales with concurrency / usage"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Network-sensitive tests may require extra tuning"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"11) LambdaTest"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Overview:"})," Cross-browser/device cloud with high-speed orchestration (HyperExecute) and on-prem grid options for strict security focused organizations"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-4 text-octo-text space-y-2",children:[s.jsx("li",{children:"Next-gen test orchestration cloud to speed up test execution"}),s.jsx("li",{children:"10,000+ real devices and browsers"}),s.jsx("li",{children:"On-premise Selenium Grid for regulated environments"}),s.jsx("li",{children:"AI-native test intelligence insights"}),s.jsx("li",{children:"Professional services options"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("span",{className:"text-octo-purple-light font-semibold",children:"Designed for:"})," Enterprises balancing speed with network / data residency constraints"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Benefits / downsides:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Very fast parallelization and smart scheduling"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," On-prem path for sensitive workloads"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-green-400",children:"+"})," Broad range of native integrations"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," Unclear module based pricing, scales with test volume, enterprise quote based"]}),s.jsxs("li",{children:[s.jsx("span",{className:"text-red-400",children:"–"})," You'll still own test design / quality strategy"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"How to choose (quick guide)"}),s.jsxs("ul",{className:"list-disc pl-6 mb-8 text-octo-text space-y-3",children:[s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"Need the fastest path to a stable suite (incl. out-of-the-box infra)?"})," ","Start with Octomind or Mabl."]}),s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"Already standardized on code-first testing?"})," Go with Playwright + a device / browser cloud (Sauce Labs/ BrowserStack/ LambdaTest)."]}),s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"Heavy SAP/ERP/CRM testing needs?"})," Use Tricentis."]}),s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"Prefer clouds with private options or on-prem grids?"})," Sauce Labs private cloud, LambdaTest on-prem are good options."]})]}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const Ee=({name:e,title:t,imageSrc:n})=>(e.split(" ").map(r=>r[0]).join("").toUpperCase(),s.jsxs("div",{className:"mt-12",children:[s.jsx("img",{src:n,alt:e,className:"w-48 h-48 object-cover rounded-lg"}),s.jsx("p",{className:"mt-2",style:{fontSize:"14px",fontWeight:700,color:"#8E8EE7"},children:e}),s.jsx("p",{style:{fontSize:"14px",color:"#C3C9D3"},children:t})]})),HD=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"why agents DO NOT write most of our code - a reality check"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:`At Octomind, we build AI agents - but our code is still mostly written by humans. We love LLMs and use them everywhere we can, from our product to our internal workflows. But despite all the hype, we're nowhere near "agents writing most of our code."`}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:["We have good reasons for not jumping on the bandwagon of companies like"," ",s.jsx("a",{href:"https://youtu.be/zDmW5hJPsvQ?t=1100",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Anthropic (generating 80%)"}),","," ",s.jsx("a",{href:"https://www.cnbc.com/2025/04/29/satya-nadella-says-as-much-as-30percent-of-microsoft-code-is-written-by-ai.html",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Microsoft (30%)"})," ","or"," ",s.jsx("a",{href:"https://www.forbes.com/sites/jackkelly/2024/11/01/ai-code-and-the-future-of-software-engineers/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Google (25%)"})," ","just yet."]}),s.jsxs("div",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/agents-linkedin-post.png",alt:"LinkedIn post screenshot",className:"rounded-lg border border-border mx-auto max-w-2xl w-full"}),s.jsxs("p",{className:"text-center text-sm text-muted-foreground mt-2",children:["one of many, source:"," ",s.jsx("a",{href:"https://www.linkedin.com/posts/rhunterharris_your-best-developers-should-be-leading-not-activity-7379856503962497025-Wv9A/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"LinkedIn"})]})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-8",children:`A few vital things are "not there" yet. Here's why it matters - and what it takes to actually close the gap.`}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"Experimenting with coding agents in day to day coding"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["We've been using ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"}),","," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Claude Code"}),", and"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Windsurf"})," for months, but none of us can honestly say they've boosted our productivity in a meaningful way (say, 20% or more). Sure, tab completions are often solid, and I've had some success getting them to generate unit tests - especially when there's existing tests to copy from (like new routes)."]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Still, that's nowhere near the 80%+ efficiency gains others claim. So, driven by equal parts FOMO and curiosity, my colleague Fabio and I decided to spend the past week implementing a roadmap feature"," ",s.jsx("em",{className:"text-octo-teal",children:"entirely"})," with AI."]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:["Before diving in, we combed through the documentation for our preferred tools to make sure we weren't missing anything useful. We also updated our Cursor rules and"," ",s.jsx("a",{href:"http://claude.md/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"CLAUDE.md"})," ","file to inject updated knowledge about our product and our dev workflow, enabled"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"BugBot"})," for AI code reviews and went to work."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"The feature we tried to build (with AI)"}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"At Octomind, we build an agent-powered end-to-end testing platform. Our tests aren't tied to branches - they live centrally in our system, which doesn't support branch-specific versions of test cases. That works fine until you start using branch deployments."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Picture a SaaS app with three tests: login, create post, edit post. The app under test is developed with branch deployments for each pull request (PR). Now imagine a branch that changes the login flow - say, it adds 2FA. The existing login test (which only checks username + password) will now fail, blocking the pipeline for that PR."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"At the moment, you've got two options:"}),s.jsxs("ol",{className:"list-decimal list-inside text-lg text-muted-foreground mb-6 space-y-2",children:[s.jsx("li",{children:"Remove the failing test so it doesn't block unrelated PRs, fix it manually (or via AI) to handle the new flow, merge, then re-enable it."}),s.jsx("li",{children:"Update the test directly and merge your PR - but now every other dev's pipeline breaks until you're done."})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Neither is great. One blocks others; the other breaks trust in your merge."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"To combat this, we wanted to extend the concept of branches to our tests. When a branch is created, you can spawn a branch-specific copy of a test. That copy runs only for that branch and can be edited freely. When the branch merges, the copy becomes the new default."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-8",children:"We figured this feature should be doable in about a week with two developers."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"First try: Running wild"}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"As the first iteration, we let the agents roam. We did not expect this to work perfectly, but we wanted to see where it's at."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:`We've got a decent-sized monorepo, so "just dump everything into context" isn't an option. We take testing seriously and have guardrails the AI can use to check its own output.`}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"So I wrote a detailed brief and attached the files it needed into the context. This wasn't a 'tiny prompt performing miracle' thing - I iterated the prompt until it was as specific as possible. Within ~5 minutes, the agent produced a plan with reasonable 11 TODOs:"}),s.jsxs("div",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/agents-cursor-plan.png",alt:"Cursor agent produced a coding plan",className:"rounded-lg border border-border mx-auto max-w-2xl w-full"}),s.jsx("p",{className:"text-center text-sm text-muted-foreground mt-2",children:"Cursor agent produced a coding plan"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"We hit `run` and that's where it went sideways. The agent started cranking out lines, but stumbled on basics any dev would breeze through - like regenerating the Prisma client after a database schema change (yes, the Cursor rules spelled it out)."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:`I went back and forth with it several times for clarification. It reported success with the message: "The feature should now work correctly! The fork button should be functional, and you'll be able to see forked test cases using the new filter. 🎉" while:`}),s.jsxs("ul",{className:"list-disc list-inside text-lg text-muted-foreground mb-6 space-y-2",children:[s.jsx("li",{children:"Not checking off all the TODOs"}),s.jsx("li",{children:"Producing nothing that worked on our dev server (which it had access to via computer use)"}),s.jsx("li",{children:"Ignoring basic coding guidelines we explicitly listed"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"An incomplete list of misses that wouldn't trip up a human dev here:"}),s.jsxs("ul",{className:"list-disc list-inside text-lg text-muted-foreground mb-6 space-y-2",children:[s.jsx("li",{children:"Built a React component for new buttons… and never wired it into existing components"}),s.jsx("li",{children:"Skipped our standard logging library"}),s.jsx("li",{children:"Used very inefficient database queries (it made an extra request for every ID that is joined anywhere)"}),s.jsx("li",{children:"Ignored our naming and structure conventions"}),s.jsx("li",{children:"Added two new external libs for trivial stuff we already have"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-8",children:`and I am not even talking about bugs here. These are just things that immediately stick out and would not have happened to a developer. Yes, we tried multiple rounds of "still doesn't work - you forgot to actually use the new button" and similar nudges. The result: a 2,000-line PR that needs review and rework almost everywhere.`}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"Take two: Smaller, incremental changes"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["I decided to start over. We never expected this to just work - these agents are supposedly better at smaller features anyway. So I stepped back and thought through how I'd actually build it myself, step by step. This kind of ideation is where I usually ",s.jsx("em",{className:"text-octo-teal",children:"do like using an LLM"})," - not to code, but to bounce around approaches in planning mode."]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Then I had it make a plan again, this time for just the first piece:"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"loading the correct test from the database given an execution URL"}),"."]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"It produced another long, seemingly sensible plan. I let it run - expecting to step in more often this time. After running through several contexts worth of chat, I had another pull request."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"About 1,200 lines of code, just for this one part. Does it work? Well… it typechecks. Beyond that, who knows."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"In the spirit of the experiment, I handed the PR to BugBot for review. It left four comments - one of them pointing out that transaction handling was broken. That's not great for data consistency, so I fed the comment back into the chat."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"The AI's response:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-6",children:s.jsx("span",{className:"text-octo-teal",children:'"Of course, massive oversight on my part, I apologize. Let me fix it: …"'})}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"Apology accepted, I guess. It did patch a few places, and when I asked if it saw any more issues, it replied:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-6",children:s.jsxs("span",{className:"text-octo-teal",children:[s.jsx("strong",{children:"Recommendation"}),s.jsx("br",{}),"- Fix Transaction Consistency: Ensure all database calls within getFullTestCasesWithPrerequisites use the same transaction client. (and … 7 more things)"]})}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"All of this was in a single file. The only context it needed was the database schema - literally the thing it was just working on."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"It also has a habit of making life easy for itself:"}),s.jsx("div",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/agents-cursor-response-1.png",alt:"A cursor coding agent response",className:"rounded-lg border border-border mx-auto max-w-2xl w-full"})}),s.jsx("p",{className:"text-lg text-muted-foreground mb-8",children:"It'll check for linter errors, but only by running head -30 and some regex filters, so it convinces itself everything's fine."}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:['Confidently marking half-done work as finished, apologizing for "massive oversights," fixing things only to break others (look up the German word'," ",s.jsx("a",{href:"https://www.reddit.com/r/German/comments/6pp1nb/word_of_the_day_verschlimmbessern/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"verschlimmbessern"}),"), and completely ignoring our existing designs and UX is not even the worst part."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"The issues that really matter"}),s.jsx("h3",{className:"text-xl md:text-2xl font-bold text-foreground mt-8 mb-4",children:"1. Loss of mental model"}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:`Let's say the agent can now ship medium-complexity features with little help. And let's even assume we've fixed the "wait 3 minutes, review 1000 lines of output" problem by turning developers into orchestrators of agents instead of coders. That's the dream many LinkedIn posts are selling.`}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Even then, a huge issue remains:"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"I lose my mental model of the codebase"}),"."]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Right now, I know how changing one part affects another, where bugs tend to hide, and how the data model behaves. That intuition disappears when the AI keeps dropping thousand-line PRs that might even get auto-merged. When a teammate does that, I can trust they made thoughtful trade-offs, and I'll pick up context while reviewing or building on top of it. With AI, that learning loop is gone."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"So when a tough bug or edge-case feature comes up - the kind the AI still can't handle - it feels like I'm seeing the codebase for the first time. Maybe I skimmed some reviews of features I know the AI can do by itself (unless I ran auto-review tools like CodeRabbit), but that's nowhere near the understanding that comes from actually interacting with the code yourself."}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:["Until I can trust the AI completely, I need to keep my own mental model alive. Otherwise,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"every time I need to do something myself feels like joining a new company"}),"."]}),s.jsx("h3",{className:"text-xl md:text-2xl font-bold text-foreground mt-8 mb-4",children:"2. Absence of self-reflection"}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"AI is currently bad at judging its own performance. Constant replies like these are only annoying if you let them get to you, but boy, is it hard not to:"}),s.jsxs("div",{className:"my-8 space-y-4",children:[s.jsx("img",{src:"/assets/blogs/agents-cursor-response-2.png",alt:"A cursor coding agent response",className:"rounded-lg border border-border mx-auto max-w-2xl w-full"}),s.jsx("img",{src:"/assets/blogs/agents-cursor-response-3.png",alt:"A cursor coding agent response",className:"rounded-lg border border-border mx-auto max-w-2xl w-full"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"I asked it to do so before implementing the feature, to which it responded with:"}),s.jsx("blockquote",{className:"border-l-4 pl-4 border-white italic text-muted-foreground mb-6",children:s.jsxs("span",{className:"text-octo-teal",children:["MODERATE CONFIDENCE - I can implement this correctly, but with some caveats:",s.jsx("br",{}),s.jsx("br",{}),"Overall Assessment: I can implement this task correctly, but it would require careful attention to detail, thorough testing, and potentially some clarification on the missing methods and business logic. The complexity is manageable but not trivial."]})}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["That sounds like a human engineer's self-assessment, because the model is parroting human training data. The problem is that it shouldn't use the training that is made by humans (",s.jsx("a",{href:"https://arxiv.org/abs/2502.00007",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"at least until recently"}),") to judge its own ability, because it is not a human."]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["And that's the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"core issue: the model has no concept of its limits"}),`. You only find out if it can do the task by letting it try. An intern can say, "I've never done this." An LLM doing so is very unlikely.`]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-4",children:"Worse, on our smaller follow-up task it graded itself even higher:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-8",children:s.jsxs("span",{className:"text-octo-teal",children:["COMPLEXITY: HIGH - This is a substantial refactoring",s.jsx("br",{}),s.jsx("br",{}),"MY ABILITY: HIGH CONFIDENCE - I can definitely implement this correctly because:",s.jsxs("ul",{className:"list-disc list-inside mt-2 space-y-1",children:[s.jsx("li",{children:"The plan is very detailed and specific about what needs to be changed"}),s.jsx("li",{children:"I understand the current architecture and data flow"}),s.jsx("li",{children:"The changes follow established patterns in the codebase"}),s.jsx("li",{children:"The implementation steps are clearly outlined"})]}),s.jsx("br",{}),"The task is definitely implementable and I have high confidence I can complete it correctly - it's just a matter of following the detailed plan step by step and implementing all the missing pieces."]})}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-foreground mt-12 mb-6",children:"The good parts of coding agents"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["AI absolutely has a place in a developer's toolbox. I use ChatGPT or Cursor's Ask mode daily - to brainstorm, debug, or get unstuck on smaller problems. Tab completions? They're right about 80% of the time, which is good enough to keep them on. I even let AI handle things like writing unit tests for clean interfaces or refactoring small snippets. Wrapping a loop in"," ",s.jsx("a",{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"`Promise.allSettled`"})," ","is boring for me - but trivial and instant for AI. It's also great at recreating well-known patterns from scratch - like traversing a tree structure."]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"And for non-technical users, automation powered by AI can be a massive unlock. That's literally what we work on at Octomind: automating a technical task, but within well-defined boundaries using specialized agents. They don't write the whole codebase; they handle narrow, observable parts of it where output constraints keep them in check."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Other focused tools can deliver similar value. And sure, maybe one day AI will truly handle everything it's being credited with today (whether that's LLMs or something beyond them)."}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:["But we're not there yet - and"," ",s.jsx("a",{href:"https://www.linkedin.com/posts/alekseystukalov_yesterday-i-cleaned-up-about-60-of-code-activity-7388384145430822912-G433",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"more people are starting to admit it"}),"."]}),s.jsx(Ee,{name:"Veith Röthlingshöfer",title:"AI engineer at Octomind",imageSrc:"/assets/blogs/veith-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]});function qD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-black text-[#15D7AB] mb-4 leading-tight lowercase",children:"tests created with every PR"}),s.jsx("p",{className:"text-octo-text text-lg mb-12",children:"Octomind Product Update, October 23, 2025"}),s.jsxs("div",{className:"animate-fade-in-up prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"There are faster ways of creating end-to-end tests in bulk beyond hand scripting. Octomind has several - prompting, recording, generating from your other tools via MCP. But to 'shift testing left' in a scaling scenario, we needed to come up with something even faster."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-12",children:"Like out-of-the-box test batch generation from your code."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-2",children:"Generate batches of tests when committed code"}),s.jsx("p",{className:"text-octo-text mb-6",children:"Octomind generates relevant tests from the new feature code automatically"}),s.jsx("img",{src:"/assets/blogs/product-update-25-10-23-github-pr.png",alt:"A screenshot of a GitHub CI pipeline - a PR with integrated Octomind batch generation launched at commit",className:"w-full rounded-lg mb-6"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"When you commit new code, we start generating relevant tests for the new functionality automatically. Linked with PR comment - you can preview the test batch generation in the Octomind app and make adjustments if necessary. After the batch of tests has been generated and validated, we add the tests to your suite of active tests and comment back to your pipeline."}),s.jsx("img",{src:"/assets/blogs/product-update-25-10-23-batch-generations.png",alt:"Test batch generation in the Octomind app",className:"w-full rounded-lg mb-6"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"As of today we do batch generation via GitHub Actions integration only. Let me know if you're using a different CI/CD and would be interested in using the feature."}),s.jsx("div",{className:"mb-12",children:s.jsx("a",{href:"https://octomind.dev/docs/integrations/quickstart-GitHub",target:"_blank",rel:"noopener noreferrer",className:"text-[#8E8EE7] hover:text-[#15D7AB] transition-colors",children:"See how it works →"})}),s.jsx("div",{className:"border-t border-white/10 my-12"}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-2",children:"We've started shipping test analytics"}),s.jsx("p",{className:"text-octo-text mb-6",children:'New "project health" insight in your overview'}),s.jsx("img",{src:"/assets/blogs/product-update-25-10-23-project-health.png",alt:"Project health graph insight in the Octomind app",className:"w-full rounded-lg mb-6"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"A first shot of the run history of your e2e test suite appears now in your project overview. Successes and failures over time in a handy summary graph. We have several requested insights, we'll work on next."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"What dashboard would help you to get a quick overview of your test suite?"}),s.jsx("div",{className:"mb-12",children:s.jsx("a",{href:"https://app.octomind.dev/",target:"_blank",rel:"noopener noreferrer",className:"text-[#8E8EE7] hover:text-[#15D7AB] transition-colors",children:"Go to app and check it out! →"})}),s.jsx("div",{className:"border-t border-white/10 my-12"}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-2",children:"What is the octomind design system?"}),s.jsx("p",{className:"text-octo-text mb-6",children:"A sneak peek into our UI design by our lead designer"}),s.jsx("img",{src:"/assets/blogs/product-update-25-10-23-design-system.png",alt:"A screenshot from the octomind design manual",className:"w-full rounded-lg mb-6"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Our newest blog is something for UX/UI aficionados. Octomind is a software testing SaaS platform with a fairly sophisticated UI. From the very beginning, we put effort into a good UX/UI. Design of the product was an inevitable part of it."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Our Daniel Z. Aczel has written about how we've built our design system."}),s.jsx("div",{className:"mb-12",children:s.jsx("a",{href:"/blog/what-is-the-octomind-design-system",target:"_blank",rel:"noopener noreferrer",className:"text-[#8E8EE7] hover:text-[#15D7AB] transition-colors",children:"Read Daniel's blog →"})}),s.jsx("div",{className:"border-t border-white/10 my-12"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"We are in the process of rethinking content informing you about our product right now. Is there anything you're missing, anything you'll like to change or add to this product update?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"I'm looking forward to your feedback!"}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"})]})]})}),s.jsx(se,{})]})}function VD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-[#15D7AB] mb-8 leading-tight animate-fade-in-up",children:"what is the octomind design system?"}),s.jsx("div",{className:"mb-8 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/product-update-25-10-23-design-system.png",alt:"a screenshot from the octomind design manual",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Octomind is a software testing SaaS platform with fairly sophisticated UI. From the very beginning, we put effort into a good UX/UI. Design of the product was an inevitable part of it. Here are some details about how we've built our design system."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:['Good "how we made" stories tend to contain some kind of twist and relevation about how we came up with a new approach to design systems and failed many times, but learned from our failures and finally ended up with a better version or something. But the truth is -'," ",s.jsx("strong",{className:"text-octo-purple-light",children:"we knew how to build a good design system, we designed it, we built it, it worked like a charm, end of story."})," ","Here are some details about it if you are into this kind of thing:"]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The Octo design system foundation"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The foundation layer contained the following tokens:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2 italic",children:[s.jsx("li",{className:"text-[#15D7AB]",children:"colors"}),s.jsx("li",{className:"text-[#15D7AB]",children:"typography"}),s.jsx("li",{className:"text-[#15D7AB]",children:"effects"}),s.jsx("li",{className:"text-[#15D7AB]",children:"paddings and margins"})]}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"Colors and tokens"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Our colors were very carefully designed not just to look good, but to be perfectly functioning as tokens."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"The color tokens had two layers. Base tokens for the raw colors and functional tokens, this way they can easily updated in isolation if needed, but still only in one central place."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Some interesting mechanics that play well together:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"every second layer token was using the same value base token as a reference for the same functionality. Example: text tokens are derived from value 20 tokens and background tokens are derived from value 80 tokens"}),s.jsx("li",{children:"every token with a value of 50 or less should be AA compliant with dark text and every token with greater or equal value are AA compliant with light text"}),s.jsx("li",{children:"every same value token has the same level of color contrast in accordance to different value tokens, therefore it is very easy to know which can be combined with which token to achieve certain accessibility standards"})]}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-6",children:"Even if a developer randomly assigned a text style to a text on a randomly assigned background, it will always be readable in at least AA level of the WCAG accessibility standard! It was literally impossible to do less accessible UI than designed."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/design-system-colors.png",alt:"the base colors neatly arranged in columns based on their values",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-sm text-octo-text mt-2 text-center",children:"our base colors neatly arranged in columns based on their values - second layer tokens are listed below the base colors"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"There were even more colors for graphs, gradients for hero items and some transparent tokens in the system as well with a similar logic as the base colors."}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"Hierarchy"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"The hierarchy of the elements in Octo Design System was also very precisely designed so its not only easy to use, but basically dictates usage to whoever is working with it."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Typographic styles, border radiuses, effects, margins and paddings, element sizing and so on could have 3 sizes: Large, Medium or Small. From here on the logic was simple:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"on the body large value entities can go (large title, large card with a large border radius, etc...)"}),s.jsx("li",{children:"inside large elements go the medium elements"}),s.jsx("li",{children:"in medium elements go the small elements"}),s.jsx("li",{children:"nothing goes in small elements"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"These rules not only helped us create a hierarchy that makes sense to our users, but also prevented us from making unsatisfactory solutions:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsx("li",{children:"we couldn't nest infinite elements in each other, therefore our UI remained simple with a maximum of 3 levels of objects"}),s.jsx("li",{children:"we couldn't fall into the trap of UI scale-shift, where the elements are just progressively growing or shrinking as the designs evolve"})]}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-6",children:"Once again it was impossible to design or develop something that didn't meet our predefined standards."}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/design-system-hierarchy.png",alt:"hierarchy diagram",className:"w-full rounded-lg"})}),s.jsx("p",{children:"An additional visual eye-candy that I absolutely loved was that the size of border radiuses and their parent components' internal paddings were synchronized in a way that elements nested in each other always had the same centerpoint for the circular shape that defined their border radius (see octo-radius-small and octo-radius medium on the picture above). Not only this looked nice, but if something was nested in the wrong element, it was very easy to spot even for someone who is not so design-savvy."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Component layer"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Nothing extravagant here, our components followed the same sizing (L-M-S) and were hosted in Storybook where devs could try them out before using them."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/design-system-button.png",alt:"anatomy of a button",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-sm text-octo-text mt-2 italic text-center",children:"anatomy of a button"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Our next step was to create component-combinations or patterns that were commoly used groups of components."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/design-system-component.png",alt:"a simple component-combo that we used for AI prompting field all around the app",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-sm text-octo-text mt-2 text-center",children:"a simple component-combo that we used for AI prompting field all around the app"})]}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"Icons"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Icos are mostly part of the Branding topic, but from a design system perspective an interesting quality of life feature we created was the automatic syncing of the icons from Figma to our repo without any manual work. Ok, a little manual work, there was a sync button in GitHub that you needed to press."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Of course this meant that whatever was in Figma got into the app directly - we had a some mechanics to avoid large failures, but a lot of discpline was needed from the design side, therefore we wrote down some rules."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/design-system-icons.png",alt:"about 1/3 of our icon creation rules pinned in the Iconography library",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-sm text-octo-text mt-2 italic",children:"about 1/3 of our icon creation rules pinned in the Iconography library"})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Why did we decide to invest in a design system in the first place?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"A design system is one of the smartest investments you can make when building a SaaS product. It keeps your product consistent, cohesive, and fast to build. Instead of designers and developers reinventing buttons, colors, and layouts every time, everyone works from the same playbook. That means fewer design inconsistencies, faster releases, and a smoother experience for users. It also keeps your brand recognizable and your interface predictable - two things that quietly build trust and make your product feel polished."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"As your SaaS grows, a design system becomes even more valuable. It scales with you, making it easy to update visuals, maintain accessibility, and roll out new features without breaking the experience. It gives your team leverage - you move faster and spend less time fixing small UI issues. In short: a good design system helps you build better software, faster, and keeps chaos at bay as your product evolves."}),s.jsx(Ee,{name:"Dániel Z. Aczél",title:"UX lead",imageSrc:"/assets/blogs/daniel-aczel-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function GD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"software testing stack guide: from unit tests to performance"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:["The testing landscape is crowded, and developers often end up with a mix of tools that don't work well together. The result: flaky tests, duplicated effort, and limited visibility across test layers. This checklist lays out practical technology stacks for most common application types -"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"SaaS"}),","," ",s.jsx("strong",{className:"text-octo-purple-light",children:"e-commerce"}),", and"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"canvas-based"})," apps - and covers major testing categories from ",s.jsx("strong",{className:"text-octo-purple-light",children:"unit"})," to"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"API"})," testing."]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Unit testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Jest"}),": great for javascript / typescript services and React-based frontend"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"pytest"}),": reliable for Python-based microservices or backend logic"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"xUnit / NUnit"}),": proven .NET frameworks with good IDE integration"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Vitest"}),": our preferred solution fits nicely with typescript"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Mocha + Chai"}),": flexible for Node.js or custom server logic"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Integration testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Cypress component tests"}),": integrate frontend and backend in one environment"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Component testing in Storybook"}),": tests UI components in isolation, provides a sandbox environment where each component can be rendered with different states outside your main app"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"pytest + Docker Compose"}),": run backend + DB integration tests locally"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"pytest + requests"}),": simple setup for backend-to-backend or service-to-service tests"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"End-to-end testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"}),": AI-powered full-service testing platform to create, run and maintain E2E tests; AI-powered tests generation, out-of-the-box test runner, automated test failure detection and auto-fix"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Playwright"}),": modern code-first testing framework, supports multiple browsers and parallel runs"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Cypress"}),": testing framework and developer-friendly E2E runner, strong for component + browser tests"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Mobile app testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"XCUITest / SwifUITest for iOS"})," +"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Espresso for Android"}),': the "native" way to do testing']}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Appium"}),": standard open-source choice for Android / iOS automation"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"ACCELQ"}),": no-code mobile test automation platform works well for React Native apps"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"BrowserStack"}),": cloud device testing with parallel runs"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Firebase Test Lab"}),": scalable testing for native Android apps"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Cross-browser testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"LambdaTest"}),": wide range of browser / OS combos"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Playwright"}),": supports Chromium, Firefox, and WebKit in parallel"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Sauce Labs"}),": mature, scalable device / browser cloud"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Monitoring in production"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Datadog"}),": full-stack observability (infrastructure + application + user experience)"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"}),": schedule regular e2e test runs of the production environment"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Grafana Cloud (with Prometheus + Loki + Tempo)"}),": open-source-friendly, customizable observability stack"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Checkly"}),": unifies testing, monitoring, & observability with an AI-native workflow"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Accessibility testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"axe-core"}),": industry standard for accessibility rule validation"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Mabl"}),": primarily functional test automation with an accessibility testing extension so you can reuse your existing UI tests"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Lighthouse (Google)"}),": built into Chrome and available via command line / Node modules"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"API testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Postman"}),": still the go-to for teams validating service contracts"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Hoppscotch"}),": lightweight alternative to Postman"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"SoapUI"}),": functional testing tool for REST, SOAP and GraphQL APIs with an OS alternative"]})]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-3",children:"Performance testing"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Apache JMeter"}),": open-source, Java based application to load test functional behavior and measure performance"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Gatling"}),": all in one load testing for large orgs"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"k6"}),": load test checkout APIs and product search endpoints"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Chrome DevTools Tracing"}),": manual but essential for GPU-bound performance issues"]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Final advice on building a testing stack"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-teal italic",children:"Start small"})," - don't add everything at once. Begin with solid unit tests and expand upward."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-teal italic",children:"Automate what's repetitive, not what's random"})," - use AI-driven tools like Octomind for unstable, UI-heavy flows, and keep deterministic logic under code-based tests."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-teal italic",children:"Unify your reporting"})," - mix-and-match tools are fine, but feed all results into one dashboard (CI/CD, Datadog, Grafana, etc.)."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-teal italic",children:"Test early, test continuously"})," - the best testing stack is the one that runs on every commit and breaks fast when something's off."]})]}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function YD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-3xl mx-auto relative z-10",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"organize automated tests without getting eaten by your devs"}),s.jsx("div",{className:"mb-10 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/68dd4550709326863d2da163_3f79e9b1dbba5cd0185f46e4852d3be4_ChatGPT.webp",alt:"Organize automated tests",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"There's not much a developer hates more than a blocked pipeline by a flaky test. Well, maybe having to refactor someone's legacy code, but pipeline delays are right up there."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:`They're on their own deadlines, and every minute counts. If the blocker is a real bug, no one argues - better to stop a bad merge than fire-drill it in production. But if the blocker turns out to be a flaky test? That's when things get… heated. Faster than you can say "sorry," your end-to-end tests get yanked from the pipeline and you're back to hoping the right bugs get caught before prod. Not a great look.`}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:`And yet, ignoring tests isn't an option either. If you've ever had something critical fail in production because "oh yeah, that test was turned off," you know the pain. Tests need to run, and they need to run as soon in the pipeline as possible. Anything else is just a slow slide into "we'll fix it later" - a.k.a. never.`}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic text-muted-foreground mb-6",children:s.jsx("span",{className:"text-octo-teal",children:"How do you keep your pipeline trustworthy without triggering a developer revolt?"})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Here's the strategy we use at Octomind that's been keeping our devs (mostly) happy, our pipeline (mostly) green, and our releases (mostly) bug-free."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The e2e testing setup: An hourglass, not a pyramid"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:'Forget the textbook "testing pyramid." Our shape looks more like an hourglass.'}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Thousands of unit tests"}),": These are quick to write, quick to run, and cover the bulk of our low-level logic. Modern AI helps here - generating solid starting points for many cases - but we still make sure they test ",s.jsx("em",{children:"useful"})," things, not just padding our coverage stats. Unit tests are our wide base, and they don't slow anyone down."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"A pinch of integration tests"}),": Just a handful. They're trickier to write, more prone to timing issues, and not something we want to balloon in number. They cover only the most critical cross-component behaviors."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"A generous layer of e2e tests"}),": This is where things get interesting. We use our own tooling to create, run, and maintain them. Contributions come from all over - developers, QA, even business folks. The key here isn't the quantity, it's the ",s.jsx("em",{children:"stability"}),"."]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The OctoQA rotation"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:["We don't have a dedicated QA, but we consider software quality to be an essential part of every Octoneer's work. To keep stability front and center, we introduced a rotating role:"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"OctoQA"}),"."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:'Each week, a different person wears the OctoQA hat. Their mission: monitor and manage our "non-pipeline" e2e tests.'}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Why non-pipeline? Because fresh e2e tests are often flaky at first. Not because the code is bad, but because test setup is hard - isolation issues, data dependencies, timing quirks. Even seasoned test writers don't always nail it on the first try."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The test quarantine process"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"Here's how it works:"}),s.jsxs("ol",{className:"list-decimal pl-6 mb-6 text-octo-text space-y-2",children:[s.jsxs("li",{children:["New e2e test is written → It does ",s.jsx("em",{className:"text-octo-teal",children:"not"})," go straight into the blocking pipeline."]}),s.jsx("li",{children:"Instead, it is run in staging and as part of nightly scheduled runs."}),s.jsxs("li",{children:["Each morning, OctoQA reviews the results:",s.jsxs("ul",{className:"list-disc pl-6 mt-2 space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Fails?"})," Sent back to the author for fixes."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Passes 10 consecutive runs?"})," Promoted to the next environment, eventually graduating to the pipeline."]})]})]})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:['This "quarantine first, promote later" approach means our blocking pipeline stays green for the right reasons - not because we stripped all the tests out of it, but because only proven and'," ",s.jsx("em",{className:"text-octo-teal",children:"stable"})," tests make it in."]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"What happens if a pipeline test fails without cause?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"It rarely happens, but even after promotion, a test can occasionally turn flaky. In that case, we don't let it torture developers. It's immediately pulled back into quarantine for investigation. Once fixed and stable again, it can rejoin the main pipeline."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Lessons learned"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-6",children:"After running this system for a while, a few truths have become obvious:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-octo-text space-y-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trust is everything"}),": The fastest way to kill a pipeline's credibility is to have it cry wolf all the time."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"New tests need a proving ground"}),": 'Write it → ship it' is fine for unit tests, but e2e tests need to earn their way into the pipeline."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Shared responsibility works"}),': Rotating ownership means no one can shrug and say "not my problem."']}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"It's easier to promote a good test than to repair a broken reputation of end-to-end testing"}),": Once devs stop trusting your tests, getting them to take failures seriously again is a long road."]})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-6",children:["So yes, the pipeline still blocks when it has to. But it blocks for real bugs, not false alarms. That's how you avoid both broken prod releases ",s.jsx("em",{children:"and"})," angry dev mobs."]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Chief Product Officer and Co-founder",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const QD=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsx(te,{}),s.jsx("main",{className:"pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-4 md:px-6",children:[s.jsx(de,{to:"/case-studies",label:"Back to case studies"}),s.jsxs("h1",{className:"text-4xl md:text-5xl font-black text-[#15D7AB] mb-8 leading-tight animate-fade-in-up",children:["big SaaS cuts test maintenance by 83% with Octomind auto-fix",s.jsxs("p",{style:{fontSize:"20px",color:"#C3C9D3",lineHeight:"1.5"},children:[s.jsx("br",{}),"Enterprise platform SaaS solved test maintenance thanks to Octomind advanced debugging and auto-fix features"]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{style:{fontSize:"24px",fontWeight:700,marginBottom:"16px"},children:"Tl;dr"}),s.jsx("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7"},children:"A major enterprise SaaS platform was caught in a cycle of constant firefighting. 60% of their QA team's time went into keeping automated tests alive, leaving little room to expand coverage or reduce their heavy manual testing workload. With Octomind's AI-powered auto-maintenance, they reduced maintenance time by 83%, stabilized a growing test suite, and created the space to restart automation at scale."})]}),s.jsxs("blockquote",{className:"my-12 pl-6",style:{borderLeft:"3px solid white"},children:[s.jsx("p",{style:{fontSize:"20px",color:"#8E8EE7",marginBottom:"8px"},children:`"I didn't believe it at first, but auto-fix is an absolute game changer."`}),s.jsx("cite",{style:{fontSize:"14px",color:"#15D7AB",fontStyle:"normal"},children:"Rajesh, Senior SDET"})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{style:{fontSize:"28px",fontWeight:700,color:"white",marginBottom:"16px"},children:"Stuck at maintaining 2500 tests"}),s.jsxs("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"16px"},children:["This company's platform evolves quickly, with thousands of enterprise users relying on weekly releases to run their operations. But behind the scenes, the QA team was under constant strain. Out of"," ",s.jsx("strong",{style:{color:"#8E8EE7"},children:"5,500"})," functional end-to-end tests, half were automated - and even with that level of automation, keeping the suite healthy had become a challenge in itself."]}),s.jsxs("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"16px"},children:[s.jsx("strong",{style:{color:"#8E8EE7"},children:"Maintaining the 2,500 automated tests consumed so much time that there was no bandwidth left to add new ones."})," ","Manual testing stayed high simply because automation progress had stalled. Pipeline failures were a constant interruption, triggered by flakiness, small application changes, or outdated selectors. Fixing them wasn't straightforward either, inconsistent coding styles across engineers meant that even small updates could require untangling complex dependencies before a repair could be made."]}),s.jsxs("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"16px"},children:["Video recordings were uploaded to"," ",s.jsx("a",{href:"https://www.testrail.com/",target:"_blank",rel:"noopener noreferrer",style:{textDecoration:"underline"},children:"TestRail"})," ","to support failure analysis, but each failing test still had to be reviewed manually, often by someone unfamiliar with it. That meant hours spent investigating, context switching, and delaying releases - all while the next set of failures was already piling up."]}),s.jsx("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",fontStyle:"italic"},children:'"We were stuck in reactive mode," recalls Hendrik, Senior Manager, Developer Productivity. "The pipeline dictated our schedule, not the other way around."'})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{style:{fontSize:"28px",fontWeight:700,color:"white",marginBottom:"16px"},children:"Auto-maintenance and AI auto-fix: The game changers"}),s.jsx("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"24px"},children:"The CTO wanted to eliminate the root cause: the time and effort spent diagnosing and repairing failing tests. Octomind's AI-powered auto-maintenance provided exactly that."}),s.jsx("h3",{style:{fontSize:"20px",fontWeight:700,color:"white",marginBottom:"12px"},children:"Test debugging in action"}),s.jsx("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"16px"},children:"From the first run in the pipeline, test failures came with a plain-language summary describing the cause, so any engineer could quickly understand the problem without sifting through logs. Each failure was automatically categorized, whether it was a broken dependency, a slow page load, an application bug, or a selector change - giving instant clarity on what needed attention."}),s.jsx("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7",marginBottom:"16px"},children:`The "last successful run" feature made it easy to compare passing and failing versions of a test, pinpointing the exact change that caused the issue. From there, Octomind's AI proposed a targeted fix, ready for immediate approval. This transformed failure handling from hours of debugging into a quick, predictable routine, keeping the pipeline flowing and the suite stable.`})]}),s.jsxs("blockquote",{className:"my-12 pl-6",style:{borderLeft:"3px solid white"},children:[s.jsx("p",{style:{fontSize:"20px",color:"#8E8EE7",fontStyle:"italic",marginBottom:"8px"},children:'"Thanks to Octomind, we can analyze failures so much faster. And the consistency in how tests are generated means you see the problem right away."'}),s.jsx("cite",{style:{fontSize:"14px",color:"#15D7AB",fontStyle:"normal"},children:"Hendrik, Senior Manager, Developer Productivity"})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h3",{style:{fontSize:"20px",fontWeight:700,color:"white",marginBottom:"12px"},children:"AI agent auto-fixing broken tests"}),s.jsxs("p",{style:{fontSize:"16px",color:"#C3C9D3",lineHeight:"1.7"},children:[s.jsx("strong",{style:{color:"white"},children:"The real breakthrough:"})," the AI then suggested a"," ",s.jsx("strong",{style:{color:"white"},children:"targeted fix, ready for quick approval"}),". This turned what used to be hours of manual debugging into minutes, keeping the suite stable and freeing the QA team to expand automation coverage instead of constantly patching existing tests."]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{style:{fontSize:"28px",fontWeight:700,color:"white",marginBottom:"24px"},children:"The measurable impact"}),s.jsxs("div",{className:"grid grid-cols-2 gap-4 mb-8",children:[s.jsxs("div",{children:[s.jsx("h3",{style:{fontSize:"18px",fontWeight:700,marginBottom:"16px"},children:"before Octomind"}),s.jsxs("ul",{style:{fontSize:"14px",color:"#C3C9D3",lineHeight:"1.8"},children:[s.jsx("li",{className:"mb-2",children:"• 48% of QA time spent maintaining automated tests"}),s.jsx("li",{className:"mb-2",children:"• Manual testing remained high due to stalled automation efforts"}),s.jsx("li",{className:"mb-2",children:"• Frequent pipeline blocks delaying releases"}),s.jsx("li",{className:"mb-2",children:"• Hours to resolve failures"})]})]}),s.jsxs("div",{children:[s.jsx("h3",{style:{fontSize:"18px",fontWeight:700,marginBottom:"16px"},children:"after Octomind"}),s.jsxs("ul",{style:{fontSize:"14px",color:"#C3C9D3",lineHeight:"1.8"},children:[s.jsx("li",{className:"mb-2",children:"• 83% reduction in maintenance workload"}),s.jsx("li",{className:"mb-2",children:"• 180 new automated test cases added each week without backlog buildup"}),s.jsx("li",{className:"mb-2",children:"• Stable, growing test suite with faster, more reliable releases"}),s.jsx("li",{className:"mb-2",children:"• Minutes to resolve failures"})]})]})]}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/case-study-maintenance-chart.png",alt:"A graph of real project time savings on maintaining tests with Octomind",className:"w-full rounded-lg"})})]})]})}),s.jsx(se,{})]}),KD=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"absolute inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-0 left-1/4 w-[600px] h-[600px] bg-octo-purple/10 rounded-full blur-[120px]"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-[400px] h-[400px] bg-octo-purple/5 rounded-full blur-[100px]"})]}),s.jsx(te,{scrolled:!0}),s.jsxs("article",{className:"relative z-10 max-w-3xl mx-auto px-6 pt-32 pb-20",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 leading-tight animate-fade-in-up",style:{color:"#00FFCE"},children:"9 agentic end-to-end testing tools to consider in 2025"}),s.jsx("p",{className:"text-xl text-white mb-12 animate-fade-in-up",children:"Ship faster with tools that generate, run, and maintain E2E tests."}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none animate-fade-in-up",children:[s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"How we picked"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:'We focused on tools that (1) generate or maintain tests with AI / agents, (2) cover real world testing use cases and features, (3) integrate with CI/CD, and (4) reduce flakiness and maintenance toil. We grouped each by "best for," then called out key features, target users, upsides, and downsides.'}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"1) Octomind"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": Agent that explores your app, proposes flows, generates tests, runs them at scale, and self-maintains. Fit for a broad range of users - NLP based generation and maintenance for non-coding testers as well as strong developer ergonomics and feature set."]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Autonomous flow discovery and test generation from a URL or natural language"}),s.jsx("li",{children:"Built-in runner for parallel, reliable web test execution"}),s.jsx("li",{children:"Agent explains actions and loops you in only when needed"}),s.jsx("li",{children:"Advanced test maintenance tools like automated root cause analysis and auto-fix"}),s.jsx("li",{children:"CI/CD integration and broad range of features for enterprise suites"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Product teams and platform engineers owning large, fast-moving web apps."]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": 14-day trials for all paid plans"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Upsides:"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Strong agent behavior that mimics real users and discovers relevant flows so you don't have to"}),s.jsx("li",{children:"All in one platform for creating, hosting, running, and maintenance of end-to-end test suites"}),s.jsx("li",{children:"Low flake focus with self-healing and out-of-the-box stable execution"})]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Downsides:"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-8",children:[s.jsx("li",{children:"Web-centric; no support of native desktop/mobile yet"}),s.jsx("li",{children:"Newer ecosystem vs. legacy suites"})]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"2) Tricentis Testim"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light font-semibold mb-2",children:'Overview ("best for")'}),": best for enterprises with complex testing needs, including iOS and Android testing"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"AI auto-improved locators and automated waits"}),s.jsx("li",{children:"Low-code authoring with code extensibility"}),s.jsx("li",{children:"TestOps for governance and analytics"}),s.jsx("li",{children:"CI integrations"}),s.jsx("li",{children:"Parallel execution in the cloud"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Enterprises standardizing on Tricentis or needing hardened locator AI"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": 7 day free trial option available via Tricentis"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Mature tooling; governance features"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Heavier platform; can feel prescriptive"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"3) Mabl"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": low-code web E2E with AI assist, best for a broad range of testing incl. accessibility and performance testing"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Low-code test creation with AI assistance"}),s.jsx("li",{children:"Cross-browser / cloud execution"}),s.jsx("li",{children:"Jira, Slack, MS Teams integrations"}),s.jsx("li",{children:"Automated regression testing"}),s.jsx("li",{children:"Reporting and failure insights"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": SaaS teams wanting low-code plus CI"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": 14-day on demand free trial noted on mabl materials; pricing is quote-based"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": email and PDF testing; strong cloud runner, expansive support"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Difficulties for more complex apps, apps heavy with third-party integrations"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"4) Functionize"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),': NLP authoring and "digital worker" agents']}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:'Natural-language and "Architect" AI test creation'}),s.jsx("li",{children:"Self-healing to cut maintenance"}),s.jsx("li",{children:"Parallel cross-browser runs"}),s.jsx("li",{children:"Analytics for root-cause clues"}),s.jsx("li",{children:"Enterprise support and onboarding"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Enterprises adopting NLP based testing"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": Guided free trial on request"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Strong NLP + enterprise posture"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Unclear use of AI and human interventions in the processes"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"5) Virtuoso"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": best for business systems testing such as CRM, ERP, and Policy & Claims"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Natural-language authoring"}),s.jsx("li",{children:"Test reporting and analytics"}),s.jsx("li",{children:"Generative AI for test data"}),s.jsx("li",{children:"RPA-style flow business process automation options"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Teams with 3rd party business systems testing needs, incl. finance and insurance SaaS"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": No instant trial; sales-led entry"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Salesforce, MS Dynamics, Oracle Cloud, Workday testing"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Limited test runner functionality"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"6) Testsigma"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": best for multi-surface agentic testing (web, mobile, API)"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Agentic model across generate / run /analyze / heal"}),s.jsx("li",{children:"No-code test creation"}),s.jsx("li",{children:"Web, mobile, Salesforce coverage"}),s.jsx("li",{children:"Reporting and analytics"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Orgs consolidating tools across surfaces."]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": Trial and freemium option available based on functionality"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Broad surface area in one platform"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Depth per surface varies; evaluate against your stack"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"7) Katalon"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": best for mixed code / no-code for enterprise teams"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Web, API, mobile, desktop coverage"}),s.jsx("li",{children:"AI assistance for test creation and test healing"}),s.jsx("li",{children:"Advanced reporting"}),s.jsx("li",{children:"Extensive test execution functionality"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Teams mixing SDET code and no-code testers"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": free tier and trial for paid tiers available"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Cost-effective entry; wide protocol support, versatile test runner"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Platform breadth can be overwhelming to tune, non-AI-native tooling"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"8) Rainforest QA"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": no-code E2E tool for teams with less technical knowledge"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"No-code test creation based on natural language instructions"}),s.jsx("li",{children:"Test runner with parallel testing"}),s.jsx("li",{children:"Results and flakiness management"}),s.jsx("li",{children:"CLI, Jira, Slack and MS teams integration"}),s.jsx("li",{children:"Optional managed QA services"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": SaaS teams looking for hassle free low code testing"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),': "Request a free trial" path; pricing often quote-based; AWS marketplace lists managed tiers']}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": Simple no-code test creation; service backstop if you need coverage fast."]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Costs can skew enterprise; evaluate run economics"]}),s.jsx("h2",{className:"text-white text-2xl font-bold mt-12 mb-4",children:"9) QA Wolf"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:'Overview ("best for")'}),": best for handing off E2E to a managed Playwright team"]}),s.jsx("p",{className:"text-octo-purple-light font-semibold mb-2",children:"Key features"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Managed service builds Playwright tests for you"}),s.jsx("li",{children:"Runs in their cloud with unlimited test runs"}),s.jsx("li",{children:"24/7 maintenance and zero-flake guarantee"}),s.jsx("li",{children:"Coverage targets (e.g., ~80% in months)"}),s.jsx("li",{children:"Reporting and triage included"})]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Designed for"}),": Teams that want testing outcomes without mastering tools"]}),s.jsxs("p",{className:"text-octo-text mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Trial info"}),": Sales-led pricing per request"]}),s.jsxs("p",{className:"text-octo-text mb-2",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Upsides"}),": High coverage without hiring; hands-off service"]}),s.jsxs("p",{className:"text-octo-text mb-8",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Downsides"}),": Vendor lock-in feel; per-test pricing can climb with scope"]}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-12 mb-4",children:"To summarize"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:'"Agentic" today ranges from self-healing locators to full autonomous flow discovery. Try a short PoC on your real app and CI to compare flake rates, maintenance burden, and true run cost.'}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:`If you're mostly web and want maximum speed to value, start with Octomind, Testim, or Mabl. For platforms beyond the browser, evaluate Testsigma and Katalon. For "not having to care about any of it" QA Wolf is compelling.`})]}),s.jsx(ge,{})]}),s.jsx(se,{})]}),XD=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsx(te,{}),s.jsx("main",{className:"pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-4 md:px-6",children:[s.jsx(de,{to:"/case-studies",label:"Back to case studies"}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-[#15D7AB] mb-6 leading-tight",children:"big SaaS cut $300K in QA costs and quadrupled automation"}),s.jsx("p",{className:"text-xl text-[#C3C9D3] mb-12",children:"Using Octomind for high volume test automation of a large SaaS application"})]}),s.jsx("hr",{className:"border-white/20 mb-12"}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Tl;dr"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"A rapidly scaling B2B SaaS company was stuck in a costly and slow testing cycle, spending more than $300,000 annually on a vendor-locked provider while struggling to keep pace with twice-weekly releases. Their QA process was a bottleneck - test automation couldn't keep up and test maintenance dragged. Octomind's AI-powered QA automation platform helped the company quadruple its automation throughput, cut execution times from days to hours, and gain full ownership of their test suite."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold mb-4",children:"8,000 tests, 6,000 still manual"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"The company's product was evolving faster than their testing process could match. Twice-weekly releases demanded precision and speed, yet every cycle began with a rush of manual checks and mounting pressure. Out of 8,000 functional end-to-end tests, only 2,000 were automated. The rest relied on testers spread across three continents, following a costly and complex follow-the-sun model that was difficult to coordinate."}),s.jsxs("p",{className:"text-base leading-relaxed mb-4",children:["To bridge the gap, they brought in a"," ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"Testing-as-a-Service (TaaS) provider"}),". Instead of accelerating things, it slowed them down. The provider's setup existed outside their normal workflows, broken tests took up to 48 hours to fix. Worst of all - they didn't own the code for the automated tests."]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"The bottleneck looked like this:"}),s.jsxs("ul",{className:"text-base text-[#C3C9D3] leading-relaxed mb-6 space-y-2",children:[s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsxs("span",{children:[s.jsx("em",{className:"text-[#15D7AB]",children:"8,000 functional tests"}),", out of which were"," ",s.jsx("em",{className:"text-[#15D7AB]",children:"6,000 still manual"})]})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsx("span",{children:"Heavy reliance on global manual teams to meet coverage goals"})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsx("span",{children:"Twice-weekly releases frequently delayed by slow test fixes"})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsx("span",{children:"A locked-in relationship with a vendor who controlled the test code"})]})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"James, Senior QA Engineer, recalls the frustration:"}),s.jsx("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:s.jsx("p",{className:"text-xl text-[#8E8EE7]",children:`"Every time a test failed, we'd lose momentum. Two days to fix a broken case might not sound like much, but when you release twice a week, it's crippling."`})})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Evaluating new test automation providers"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"The QA leadership team knew they couldn't keep pace with product growth under the current model. They ran a proof-of-concept with several automation providers, looking for one that could deliver speed, control, and ease of use."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Octomind stood out for its combination of AI-powered test creation, Playwright-based code that the customer fully owns, and deep integration into existing workflows."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"Once chosen, the rollout was almost immediate. Octomind was connected to GitHub Actions within minutes, allowing the team to run automated tests across development, staging, and production environments from day one. Sharding by tags enabled hundreds of tests to execute in parallel, turning what used to be a multi-day process into a task that now completes in hours."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Automation made by non-coders"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-6",children:"Onboarding was fast and intuitive. Even junior manual testers were creating repeatable automated cases in a short time thanks to Octomind's natural language interface and visual editor. Non-technical team members were able to define, refine, and run tests without touching code. When prompting wasn't enough to fully capture a scenario, the advanced recorder served as a backup - letting testers walk through a flow step-by-step and turn it into an automated test case instantly."}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:`"Before, automation felt like a specialist's job. Now, anyone on the team can add value directly. That's changed the way we work together."`}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"James, Senior QA Engineer"})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"Octomind also introduced prompt optimization creating a standardized prompt format from each tester's initial inputs and asking clarifying questions to ensure complete coverage. This not only improved test quality but also made cases easy to understand when reviewing work created by others."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Keeping pace through AI maintenance"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"With a constantly evolving product, test breakages are inevitable. In the past, each one meant delays, frustration, and long ticket threads with the TaaS provider."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mt-4",children:"Octomind's AI-driven maintenance changed that dynamic, detecting flaky tests, proposing fixes, and letting the QA team approve them with minimal effort. Instead of spending hours diagnosing failures, they now spend minutes confirming the solution."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-6",children:"The measurable impact"}),s.jsxs("ul",{className:"text-base text-[#C3C9D3] leading-relaxed space-y-4 mb-8",children:[s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsxs("span",{children:["Automation throughput rose from ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"120 new test cases"})," a month to ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"120 each week"})]})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsxs("span",{children:["Execution time fell from ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"multiple days"})," to just a"," ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"few hours"})]})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsxs("span",{children:["Capacity worth roughly ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"$300,000 per year"})," was freed up by halving the time seven US-based SDETs spent diagnosing and fixing failing tests, and enabling thirty manual QAs across three continents to focus their regained hours on creating automated test cases instead of running repetitive manual checks."]})]}),s.jsxs("li",{className:"flex items-start",children:[s.jsx("span",{className:"mr-2",children:"•"}),s.jsxs("span",{children:["Release confidence improved significantly, with"," ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"fewer delays"})," and"," ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"higher stability"})," in production."]})]})]}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/case-studies/big-saas-automation-chart.png",alt:"Graph showing a SaaS case study where Octomind generates tests much faster",className:"w-full rounded-lg"})})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Next step: Automation at the source"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"With Octomind now embedded in their daily workflows, the team is moving toward generating tests at earlier stages of product development. This means integrating Octomind directly into their pull request and merge request processes, so that every change ships with its own automated validation before it reaches staging."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Although early in its rollout, initial experiments have been promising. Tests created in this way are more contextual, faster to validate, and immediately ready to run as part of the CI process."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Michael sees this as a natural evolution:"}),s.jsx("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:s.jsx("p",{className:"text-xl text-[#8E8EE7]",children:`"By building tests alongside the code, QA becomes a proactive guardrail. That's how you reach super high coverage without slowing anything down"`})})]})]})}),s.jsx(se,{})]}),JD=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsx(te,{}),s.jsx("main",{className:"pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-4 md:px-6",children:[s.jsx(de,{to:"/case-studies",label:"Back to case studies"}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-[#15D7AB] mb-6 leading-tight",children:"scaling QA to match the speed of engineering innovation"}),s.jsx("p",{className:"text-xl text-[#C3C9D3] mb-12",children:"How Deriv transformed testing with Octomind"})]}),s.jsx("hr",{className:"border-white/20 mb-12"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/case-studies/deriv-logo-group.png",alt:"Octomind x Deriv logo group",className:"max-w-md w-full"})}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Tl;dr"}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:[s.jsx("a",{href:"https://deriv.com/eu",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Deriv, one of the world's largest online brokers"}),", serves over three million users with accessible, intuitive trading every day. As its engineering teams embraced AI-driven development and accelerated product delivery, Deriv recognised the need to modernise its QA processes to keep pace. Deriv deployed Octomind to reduce test maintenance, aid non-technical team members with automation, and build a scalable, resilient QA framework to align with modern development cycles."]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold mb-4",children:"A culture built on AI innovation"}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:["As Principal Engineer Mark O'Donnell explains,"," ",s.jsx("em",{children:'"Deriv is very focused on democratising trading. The platform has to be intuitive and frictionless, and that means constant iteration and improvement."'})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"Deriv embraced AI across departments - not only in product features, but in how the company builds and tests its software. Mark, who began his career decades ago with an early fascination in AI, is now at the forefront of integrating AI tools into QA workflows."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"The testing bottleneck"}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:["Despite its mature engineering practices, Deriv's QA processes faced growing challenges as the company scaled. Test automation relied on a mix of high-code frameworks and older record-and-replay tools, which struggled to keep up with the rapid pace of UI changes."," ",s.jsx("em",{children:`"Although most of our tools were able to use 'testing friendly locators', still too many of our tests were rigid and would break when CSS or IDS would change,"`})," ","said Mark."]}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:'"We investigated some NLP-based tools but they proved too slow or inaccurate."'}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Mark O'Donnell, Principal Engineer at Deriv"})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Meanwhile, the engineering organisation was scaling rapidly with new products being developed and timelines shrinking. Traditional automation methods needed to be scaled in parallel."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"As a consequence, Deriv moved entirely away from high-code test automation tools and migrated to modern record and replay platforms that had smart locator backup strategies. Allowing tests to be quickly created by devs and testers. Due to the smart locator technology the generated tests were more robust and could survive minor UI changes."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Enter Octomind test automation"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Deriv's QA team researched tools that could generate tests from prompts, but most were ineffective and very slow at runtime."}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:["When Mark discovered Octomind, he was initially intrigued by Octomind's self-discovery feature - the tool's ability to autonomously explore a test target and generate meaningful test journeys."," ",s.jsx("em",{children:'"I remember watching it navigate a shopping cart site and buy a handbag. It was quite addictive,"'})," ","he laughed."]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"But the real value emerged as the team leaned into prompt-based testing, crafting structured, natural-language prompts to express user journeys and validate behaviours. By doing so, it held the promise of making test maintenance easier, and brought test creation closer to the domain logic of the product."}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:'"The prompt-based approach feels like the direction of the future. It offers the flexibility we need and lets us build tests that are resilient to change."'}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Mark O'Donnell, Principal Engineer at Deriv"})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Rollout"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:'Deriv began introducing Octomind into their "new architecture" initiatives, newer products built with AI-enabled development workflows. These products are evolving rapidly, often daily, and need a test solution that could evolve just as fast.'}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:["The team gradually replaced legacy tests with prompt-generated flows, using Octomind's support for test dependencies to keep things modular. After short training even non-technical team members could create functioning tests."," ",s.jsx("em",{children:`"We encouraged our team to keep prompts simple, break tests into smaller chunks, and use dependencies. Once the steps are generated, they're robust and dependable,"`})," ","Mark noted."]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"As adoption grew, Deriv integrated Octomind directly into their CI pipeline, allowing tests to be automatically executed as part of the release process."}),s.jsxs("div",{className:"mt-8",children:[s.jsx("img",{src:"/assets/case-studies/mark-odonnell-deriv.png",alt:"Mark O'Donnell profile picture",className:"w-48 h-48 object-cover rounded-lg"}),s.jsx("p",{className:"mt-4",style:{fontSize:"14px",color:"#C3C9D3"},children:"Mark O'Donnell, Principal Engineer, Deriv"})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Automation up, test maintenance down"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Now equipped with a modernised QA process and Octomind tooling, Deriv has achieved significant improvements in efficiency and scalability. Manual testers are now contributing to test automation without needing to code, while developer participation continues to grow."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Maintenance has seen the biggest improvement. Octomind's auto-fix feature adapts tests automatically in most cases or regenerates them via updated prompts in seconds and is proving itself very useful."}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"But the main thing that impresses Mark is the robustness of the generated test code:"}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:`"It is what sets Octomind apart from other LLM-based tools. It's repeatable and reliable, which is extremely important in test automation code."`}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Mark O'Donnell, Principal Engineer at Deriv"})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold text-white mb-4",children:"Next step: MCP integration"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Deriv is already planning the next phase of its QA transformation, with a focus on integrating MCP (Model Context Protocol), which will allow the team to embed domain-specific knowledge - such as existing test case banks and internal documentation directly into the prompt creation process. This will help generate more precise, context-aware tests that reflect how Deriv's products actually work in practice."}),s.jsxs("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:["The goal is to further streamline test creation while expanding the range of team members who can contribute to quality efforts. In the long term, Mark envisions an even broader shift."," ",s.jsx("em",{children:`"I see a future where product specs help define test coverage. Where quality becomes part of everyone's job within the Software Development Life Cycle. Tools like Octomind make that future realistic."`})," ","And for a company committed to democratising trading, democratising QA feels like the natural next step."]}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:'"Octomind helps us democratize testing the way Deriv democratizes trading."'}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Mark O'Donnell, Principal Engineer at Deriv"})]})]}),s.jsx("section",{className:"mb-12",children:s.jsx("div",{className:"aspect-video w-full",children:s.jsx("iframe",{className:"w-full h-full rounded-lg",src:"https://youtube.com/embed/V-ISoA0w_J8",title:"How Deriv scaled QA to match the speed of engineering with Octomind",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0})})})]})}),s.jsx(se,{})]});function ZD(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold text-octo-teal mb-8 leading-tight",children:"why devs need AI-powered e2e test automation"}),s.jsxs("div",{className:"animate-fade-in-up prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"QA-suggested changes to development workflows often get a blowback from engineering teams. Introducing AI-powered end-to-end (E2E) testing is no different. For once - it's tests - not all developers are thrilled about testing as a concept. Especially when it comes to end-to-end tests. And then the AI part? Difficult to digest."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Here are 6 reasons why we believe automated, AI-powered e2e tests belong to every development pipeline - for the benefit of both the code and the people who build it."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"1. Tests that run automatically are helpful for EVERYONE (*as long as they run in reasonable times)"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`Manual testing is like manual linting or manual type-checking: it's prone to human error and, honestly, it just doesn't scale. Automatic execution of tests within your CI pipeline ensures reliability. Without automation, many devs often think, "my change isn't significant, so I'll skip testing" - cue broken code and unhappy engineering. Automation means your tests always run. Consistency is king, and automatic testing is how you get there.`}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Of course, the execution speed is of importance, developers move fast and can't wait hours for the test suite run to finish. Well constructed test runners that parallelize without elaborate setup are your best friends."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"2. The true cost of bugs discovered later"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"It's no secret: bugs found later in the software development cycle are exponentially more expensive. Fixing a bug in staging can cost 5-6 times more than if caught in development, and in production, it's at least 10x - possibly even catastrophic if you lose users or revenue. Automatic E2E tests find issues early, save your team from costly context switching, extra ticket overhead, blocked releases, and late-night firefighting."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/6890ac77d9b83aa9339b5c70_Screenshot.png",alt:"Relative cost to fix bugs depending on the time of detection - graph showing exponential cost increase",className:"w-full rounded-xl"}),s.jsxs("figcaption",{className:"text-octo-text/60 text-sm mt-3 text-center",children:["source:"," ",s.jsx("a",{href:"https://deepsource.com/blog/exponential-cost-of-fixing-bugs",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"graph"})," ","and"," ",s.jsx("a",{href:"https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"NIST figures"})]})]}),s.jsxs("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:["3. ",s.jsx("em",{children:`"Blocking merges isn't bad - it's brilliant!"`})," Daniel Draper, lead developer"]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:`Sure, blocking merges with automated tests might initially frustrate the dev who just wants to "get their code out there." But trust us: everyone appreciates not being woken up at 2 am because of a production outage. Automated tests act as gatekeepers, preserving everyone's sleep, weekend plans, and sanity. It might seem restrictive at first, but soon enough, engineering teams eventually realize good QA saves their future selves from unnecessary headaches.`}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"4. Optimizing your tests isn't optional - it's essential"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Devs know it, QA engineers know it. Tests are production code. Treating tests with the same care and rigor as your application code ensures maintainability and scalability. Optimizing tests for speed and parallel execution might feel like extra work initially. But a fast, highly parallel test suite means quicker feedback, higher developer productivity, and more confident deployments. And AI-powered testing platforms make this optimization significantly easier and more effective."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"5. Testing isn't just QA's job - it's everyone's job"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Quality assurance isn't solely about catching bugs. It's about building a mindset where everyone contributes to quality. Tests shouldn't be something developers dread - they should be part of an engineering culture that embraces high-quality standards. Encouraging devs to write and manage tests at appropriate levels (think: testing pyramid) helps them to produce better, more reliable code. Tests not only help identify issues but also foster a shared sense of responsibility for quality across the team."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"6. Not leveraging AI in testing while using AI everywhere else is a missed opportunity"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`Everyone who ever used an LLM to generate a more complicated end-to-end test knows how inconsistent the outcomes are. But "using AI for testing" doesn't always mean to throw some code at an agent and let it generate a test for it. State-of-the-art testing platforms use AI in many different ways - taking advantage of its strengths while keeping critical functions deterministic.`}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"If you don't use AI in your testing workflow, you risk settling for slower delivery cycles, higher costs, more bugs slipping into production, and potentially burnt-out dev teams dealing with avoidable emergencies."}),s.jsx(Ee,{name:"Daniel Roedler",title:"Chief Product Officer and Co-founder",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const eR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-4 md:px-6 prose prose-invert",children:[s.jsx(de,{to:"/case-studies",label:"Back to case studies"}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-4 text-[#15D7AB] animate-fade-in-up",children:"faster QA for early movers in AI sales coaching"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8",children:"Octomind automated QA so WingRep could scale the development of their sales coaching app without the risk of buggy experience"}),s.jsx("img",{src:"/assets/case-studies/wingrep-logo-group.png",alt:"WingRep and Octomind logos",className:"w-full max-w-md mb-12"}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold text-accent mb-4",children:"Tl;dr"}),s.jsxs("p",{className:"text-muted-foreground",children:[s.jsx("a",{href:"https://wingrep.ai/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"SelfActualize.AI"}),", the company behind AI-powered sales coaching app ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"WingRep"}),", moves at a fast pace of engineering - deploying product updates daily and evolving features quickly to stay ahead in a competitive space. But that velocity created growing pressure on QA."]}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"As the team began to see bugs slip into production and struggled to keep regression tests relevant, they turned to Octomind. With Octomind's AI-powered testing and tight CI integration, SelfActualize.AI streamlined its QA process, stabilized test coverage, and enabled its lean team to confidently scale product innovation."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Building AI products at AI speed"}),s.jsxs("p",{className:"text-muted-foreground",children:["As"," ",s.jsx("a",{href:"https://linkedin.com/in/willmbennett/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"co-founder Will Bennett"})," ","puts it,"," ",s.jsx("em",{children:`"We're innovating faster than teams our size should be, and that means QA has to scale with us."`})]}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"SelfActualize.AI's product, WingRep, offers users on-demand coaching powered by AI. It records meetings, provides personalized post-call analysis, and enables human-like coaching conversations powered by insights from top executive mentors."}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"The engineering team is small but the ambition is huge. Engineers push updates almost daily, powered by tools like Copilot and OpenAI for code generation. To keep up, QA had to evolve too."}),s.jsxs("p",{className:"text-muted-foreground mt-4",children:[s.jsx("a",{href:"https://linkedin.com/in/anom-chavez-774396b7/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Anom Chavez, QA engineer"})," ","with over two decades of testing experience, joined to bring structure to testing at a time when bugs were beginning to affect user experience and investor demos."]}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-4 text-muted-foreground mt-6",children:[s.jsx("p",{className:"text-[#8E8EE7] text-lg",children:`"With developers shipping so quickly thanks to AI, we knew QA couldn't be an afterthought. We needed scalable, reliable test coverage from day one."`}),s.jsx("footer",{className:"text-[#15D7AB] mt-2 italic",children:s.jsx("em",{children:"Anom Chavez, QA Engineer, WingRep"})})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Growing QA backlog"}),s.jsx("p",{className:"text-muted-foreground",children:"Before Octomind, testing relied on manual checks, shallow scripts, and ad hoc efforts that couldn't keep pace with daily releases. Bugs were making it to production. Even though each pull request created its own transient test environment, there was no automated system to verify changes against the full user experience."}),s.jsxs("p",{className:"text-muted-foreground mt-4",children:[s.jsx("em",{children:'"As the sole QA, I was wearing every hat: tester, QA manager, release gatekeeper,"'})," said Anom."," ",s.jsx("em",{children:'"I needed a solution that could help me cover more ground, with less manual overhead."'})]}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"Anom had worked with custom frameworks in previous roles and knew how time-consuming they could be. Building a new one from scratch wasn't an option. They needed a low-friction tool that integrated with the workflow, scaled with velocity, and delivered immediate feedback on every change."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/case-studies/anom-will-wingrep.webp",alt:"Anom Chavez and Will Bennett of SelfActualize.AI",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-center text-muted-foreground mt-2",children:"Anom Chavez and Will Bennett of SelfActualize.AI"})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Octomind tests introduced"}),s.jsx("p",{className:"text-muted-foreground",children:"When SelfActualize.AI was introduced to Octomind through a trusted advisor, the fit was clear. Octomind offered exactly what they were missing: robust end-to-end testing that was easy to set up, prompt-based, and integrated directly into CI."}),s.jsxs("p",{className:"text-muted-foreground mt-4",children:[s.jsx("em",{children:`"Once I saw how Octomind's prompts worked with our site, I could quickly extend existing tests or create new ones from scratch,"`})," ","said Anom."," ",s.jsx("em",{children:'"The ability to add steps and validate them with clear screenshots made it incredibly efficient."'})]}),s.jsx("p",{className:"text-muted-foreground mt-4",children:'Anom also pointed out how much Octomind reduced maintenance overhead: "Compared to scripting everything manually, this gave me back hours. I could focus on what mattered - the quality of tests, not just the quantity."'}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"The visual clarity of test results also helped reduce friction. The ability to view every test step with screenshots made it fast to spot issues and confirm what went wrong."}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-4 text-muted-foreground mt-6",children:[s.jsx("p",{className:"text-[#8E8EE7] text-lg",children:'"The visual test history is incredibly helpful, I can quickly confirm test health and debug failures fast."'}),s.jsx("footer",{className:"text-[#15D7AB] mt-2 italic",children:s.jsx("strong",{children:s.jsx("em",{children:"Anom Chavez, QA Engineer, WingRep"})})})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"CI integration to test new builds"}),s.jsx("p",{className:"text-muted-foreground",children:"SelfActualize.AI began integrating Octomind across all staging and production workflows. Tests were tied to GitHub pull requests, running automatically against each preview environment. This gave developers fast feedback and reduced the chances of regressions slipping into production."}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"Even without a daily release, Octomind tests run every night on both staging and production to detect issues proactively. This became especially valuable as SelfActualize.AI started giving more product demos to prospective clients and investors."}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-4 text-muted-foreground mt-6",children:[s.jsx("p",{className:"text-[#8E8EE7] text-lg",children:'"Having confidence that the core flows were covered, even overnight, was huge. We could focus on the product, not firefighting bugs."'}),s.jsx("footer",{className:"text-[#15D7AB] mt-2 italic",children:s.jsx("em",{children:"Will Bennett, Co-Founder, WingRep"})})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Stable product with minimal overhead"}),s.jsx("p",{className:"text-muted-foreground",children:"Today, Octomind enables SelfActualize.AI to test faster, deploy more confidently, and maintain high product stability with minimal overhead. The once-manual QA process is now automated, visual, and integrated."}),s.jsxs("ul",{className:"list-disc list-inside text-muted-foreground mt-4 space-y-2",children:[s.jsx("li",{children:"Regression coverage has expanded significantly with little manual effort"}),s.jsx("li",{children:"Octomind's visual test results simplify triage and speed up debugging"}),s.jsx("li",{children:"The team can now deliver demos knowing production is stable"}),s.jsx("li",{children:"QA scales with engineering without slowing down the release pipeline"})]}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-4 text-muted-foreground mt-6",children:[s.jsx("p",{className:"text-[#8E8EE7] text-lg",children:'"Octomind frees me up to design better tests instead of scripting everything by hand."'}),s.jsx("footer",{className:"text-[#15D7AB] mt-2 italic",children:s.jsx("em",{children:"Anom Chavez, QA Engineer, WingRep"})})]}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-4 text-muted-foreground mt-6",children:[s.jsx("p",{className:"text-[#8E8EE7] text-lg",children:`"We're innovating faster than ever, and Octomind helps ensure our site stays stable as we scale."`}),s.jsx("footer",{className:"text-[#15D7AB] mt-2 italic",children:s.jsx("em",{children:"Will Bennett, Co-Founder, WingRep"})})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"What comes next"}),s.jsxs("p",{className:"text-muted-foreground",children:["As the team prepares to launch a mobile version of their sales coaching app WingRep,"," ",s.jsx("strong",{className:"text-[#8E8EE7]",children:"mobile testing is top of mind"}),"."," ",s.jsx("em",{children:`"We're going with React Native, and we're interested to see how Octomind can support us as we expand,"`})," ","said Will."]}),s.jsx("p",{className:"text-muted-foreground mt-4",children:"In the meantime, the team continues to refine its web testing and expand coverage with QA now seamlessly embedded in every part of their agile process."})]}),s.jsx("section",{className:"mb-12",children:s.jsx("div",{className:"aspect-video w-full",children:s.jsx("iframe",{className:"w-full h-full rounded-lg",src:"https://youtube.com/embed/7-Wn-Q7Zj4w",title:"Faster QA for early movers in AI sales coaching - an Octomind case study",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0})})})]})}),s.jsx(se,{})]}),tR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsx(te,{}),s.jsx("main",{className:"pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-4 md:px-6",children:[s.jsx(de,{to:"/case-studies",label:"Back to case studies"}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-black text-[#15D7AB] mb-6 leading-tight",children:"how Leaping AI ships faster with reliable e2e tests - without hiring a QA"}),s.jsx("p",{className:"text-xl text-[#C3C9D3] mb-12",children:"Leaping AI builds self-improving voice AI agents for phone support. But while their product evolves autonomously, their internal QA did not. Until Octomind came to help."})]}),s.jsx("hr",{className:"border-white/20 mb-12"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/case-studies/leaping-ai-logo-group.png",alt:"Octomind x Leaping AI logo group",className:"max-w-md w-full"})}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-2xl font-bold mb-4",children:"Tl;dr"}),s.jsxs("ul",{className:"list-disc list-inside space-y-2 text-base text-[#C3C9D3] leading-relaxed",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"The Constraint:"})," A tiny team (2 founders, 2 engineers) with no budget for a dedicated QA hire."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"The Pain:"})," Manual testing bottlenecked releases, and critical bugs slipped into production under high load."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"The Solution:"})," Octomind acts as a"," ",s.jsx("em",{className:"text-[#15D7AB]",children:'"Shadow QA worker,"'})," automatically generating and running 50–100+ tests on every PR."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"The Outcome:"})," Engineering velocity matches that of a large organization without the headcount."]})]})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold mb-4",children:'The Challenge: High stakes, zero QA, and "mental toll"'}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Leaping AI's engineering team is deliberately lean. With just two founders and two engineers, they didn't have the resources for a dedicated Quality Assurance role."}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:`"We don't have a separate QA person. Every time we do a release, we manually test everything. I personally do the QA. It's been quite painful."`}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Arkadiy Telegin, co-founder & CTO, Leaping AI"})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"The manual approach wasn't just slow; it was leaky. Serious bugs occasionally reached production. Arkadiy recalls a particularly stressful incident:"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-6",children:`"We would wake up and see that a lot of calls were marked as failed, but they weren't actually failed. It only happened under high load on specific replicas. It took its mental toll to figure it out."`}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"They hit a wall. Writing and maintaining their own E2E test suite was:"}),s.jsxs("ol",{className:"list-decimal list-inside space-y-2 text-base text-[#C3C9D3] leading-relaxed mb-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Too time-consuming"})," for a team focused on shipping features."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Too unstable"})," given their fast-changing codebase."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Too expensive"})," to solve by hiring dedicated staff."]})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"They needed a way to automate quality without slowing down development."})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold mb-4",children:'The Solution: A "Shadow QA" that maintains itself'}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Integrating Octomind changed their workflow immediately. Instead of engineers pausing to write tests, Octomind's AI took over."}),s.jsxs("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:[s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:`"It's like having a shadow QA worker that creates tests on all the code you write. Tests were always an afterthought before, and now it's okay if they are - because Octomind handles that for you."`}),s.jsx("cite",{className:"text-sm text-[#15D7AB] italic",children:"Arkadiy Telegin, co-founder & CTO, Leaping AI"})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Why it clicked:"}),s.jsxs("ul",{className:"list-disc list-inside space-y-2 text-base text-[#C3C9D3] leading-relaxed mb-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Automatic Generation:"})," PRs now trigger the creation of 50–100+ fresh tests tailored to the new features."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Zero Maintenance Burden:"}),' Octomind auto-updates tests as the product evolves, solving the "brittle test" problem that plagues most startups.']}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Peace of Mind:"})," Seeing hundreds of green checks on a PR gives the team the confidence to merge and deploy instantly."]})]}),s.jsx("blockquote",{className:"my-8 pl-6 border-l-[3px] border-white",children:s.jsx("p",{className:"text-xl text-[#8E8EE7] mb-2",children:'"It gives you peace of mind when you see all these tests have passed. It makes me much more confident to push new things for a release."'})})]}),s.jsxs("section",{className:"mb-12",children:[s.jsx("h2",{className:"text-[28px] font-bold mb-4",children:"Results"}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed mb-4",children:"Leaping AI now achieves the testing reliability of a large enterprise while staying lean."}),s.jsxs("ol",{className:"list-decimal list-inside space-y-2 text-base text-[#C3C9D3] leading-relaxed mb-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Zero Headcount Added:"})," They ship production-grade software without the overhead of a QA salary."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Faster Iteration Cycles:"})," Developers focus on building, not fixing broken tests."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"50-100+ Tests Per PR:"})," Deep regression testing happens automatically on every push."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-white",children:"Reduced Mental Load:"}),' No more waking up to "false failures" or production fires.']})]}),s.jsx("p",{className:"text-base text-[#C3C9D3] leading-relaxed",children:"Leaping AI's product is autonomous - continuously improving through real-world feedback. Now, thanks to Octomind, their testing workflow is too."})]}),s.jsx("section",{className:"mb-12",children:s.jsx("div",{className:"aspect-video w-full",children:s.jsx("iframe",{className:"w-full h-full rounded-lg",src:"https://www.youtube.com/embed/CcatIY3HGIc",title:"How Leaping AI Automates Testing with Octomind | Arkadiy Telegin (Co-Founder, Leaping AI)",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0})})})]})}),s.jsx(se,{})]});function sR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"why we finally allowed arbitrary waits in our tests"}),s.jsx("img",{src:"/assets/blogs/watch-octo.webp",alt:"Octopus holding a watch",className:"w-full rounded-xl mb-10 animate-fade-in-up"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:'For years we had a firm rule: no arbitrary sleeps in Octomind tests. Whenever someone asked for them, we pushed back. A hard-coded wait only papers over real bugs - and how do you even choose the "right" number? Too short and the test still flakes; too long and the whole suite drags while the bug stays hidden.'}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"We felt pretty proud of that stance… until we broke it. So what changed?"}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"The users who changed our minds"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Two customers arrived with the same head-scratcher: bugs on the page under test that only broke test automation while rage-clicking users just brushed it off."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["When the first user landed on the page under test for the first time, Playwright did the obvious thing: waited for DOMContentLoaded and dismissed the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:'"Accept cookies"'})," button. Closing that overlay coincided with the end of the page's first hydration cycle, so everything was ready and the click succeeded."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["The trouble appeared on the ",s.jsx("em",{className:"text-octo-teal",children:"next"})," navigation. Because the cookie banner is a one-shot component, it never shows up again. The framework takes care of hydration. During that window, the DOM looked complete in the markup but the JavaScript listeners that make it interactive weren't attached yet. Any click events were fired into limbo."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Humans barely notice: they click a button once, if nothing happens, they click again - and by then hydration has finished and the second click works. Automation, however, isn't so forgiving -"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Playwright clicks once and expects success."}),` From the test runner's point of view the button is "unclickable," and the entire suite becomes flaky.`]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Digging in, we found the culprit: the site uses ",s.jsx("strong",{className:"text-octo-purple-light",children:"Nuxt"})," ","with the ",s.jsx("em",{className:"text-octo-teal",children:"nuxt-delay-hydration"})," plug-in. To win Lighthouse points, the plug-in deliberately delays hydration, leaving a half-alive DOM that ignores clicks. Great for scores, terrible for test runners."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["In other words, the real bug isn't the flaky test; it's that the page lets the user interact"," ",s.jsx("em",{className:"text-octo-teal",children:"before"}),` it's actually ready. The app should either finish hydration faster or block pointer events until it's done. But when the dev team has "bigger fish to fry" and testers still need reliable automation - is where a well-placed, deterministic wait comes in.`]}),s.jsx("h3",{className:"text-xl md:text-2xl font-bold text-white mb-6 mt-10",children:"Waiting - with intent"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Here's the surprise twist: the plug-in also exposes a lifesaver -"," ",s.jsx("code",{className:"bg-octo-dark-light px-2 py-1 rounded",children:"window._$delayHydration"}),", a promise that resolves when hydration finishes. Instead of guessing a timeout, we could:"]}),s.jsx("pre",{className:"bg-octo-dark-light rounded-xl p-4 overflow-x-auto mb-6",children:s.jsx("code",{className:"text-octo-teal text-sm",children:"await page.waitForFunction(() => window._$delayHydration);"})}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:['No arbitrary sleep, no hidden bugs - just a deterministic gate that says: "OK, the page is ready; click away." We wrapped that in a "',s.jsx("strong",{children:"wait for"}),'" step and shipped it.']}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Now frameworks that surface a hydration promise get first-class support, our users get reliable tests, and we still sleep well at night."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:`"Fine - let's hide that bug for you"`}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Our second case came from a team testing a classic"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"magic-link login"}),":"]}),s.jsxs("ol",{className:"text-octo-text text-lg leading-relaxed mb-6 list-decimal list-inside space-y-2",children:[s.jsxs("li",{children:["Enter email → press ",s.jsx("code",{className:"bg-octo-dark-light px-2 py-1 rounded",children:"send code"})]}),s.jsx("li",{children:`Open the "Here's your one-time code" email`}),s.jsx("li",{children:"Copy the code, paste it back in the page"}),s.jsx("li",{children:"Celebrate as the user is logged in"})]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Humans breeze through this. Our automation… not so much. Roughly 70 % of runs failed with"," ",s.jsx("strong",{className:"text-octo-purple-light",children:'"invalid one-time code."'})," We replayed the run in our UI: right email, right timestamp, code typed perfectly. Nothing obvious."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["It was time to check Playwright Trace Viewer for more details. There, hidden in the network tab, we saw it: the POST request sent oneTimeCode: ",s.jsx("code",{className:"bg-octo-dark-light px-2 py-1 rounded",children:"null"})," ","even though the input showed the correct value. After a few experiments we found that if we waited ~3 seconds before filling the email field on step 1, the bug never appeared."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Classic timing issue. The fix belongs in the app, but the responsible dev team couldn't reproduce it outside automation and, frankly, had other stuff to do. Meanwhile, QA needed a working login test"," ",s.jsx("em",{className:"text-octo-teal",children:"today"}),"."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"So we asked ourselves:"}),s.jsxs("ul",{className:"text-octo-text text-lg leading-relaxed mb-6 list-disc list-inside space-y-2",children:[s.jsx("li",{children:"Is three seconds reliable? (Yes.)"}),s.jsx("li",{children:"Does it unblock the customer? (Absolutely.)"}),s.jsx("li",{children:"Does it risk future flakiness? (Maybe, but we'll monitor.)"})]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:['Result: we added a "',s.jsx("em",{className:"text-octo-teal",children:"wait for fixed time"}),`" to the "wait for" step that pauses exactly as long as the user specifies. It's a band-aid, sure, but it keeps their CI green while the real bug sits in the backlog - and that, for now, is the difference between testing and not testing at all.`]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"What we learned"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Inside your own repo, you can stay pure: spot the timing issue, fix the code, push to prod, done. But once you're shipping a testing platform for others, the equation changes. Testers aren't always sitting next to the developers who own the bug - and even if they are, business priorities win over elegance."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"So we've learned to balance principle with pragmatism:"}),s.jsxs("ul",{className:"text-octo-text text-lg leading-relaxed mb-6 list-disc list-inside space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Idealism:"})," Root‐cause every failure and fix it at the source."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Reality:"})," Octomind users sometimes lack code access, dev bandwidth, or both"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Middle ground:"})," Offer a surgical wait that unblocks the workflow while the real fix makes its way through the backlog."]})]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"It's not the romantic story we once told ourselves, but it keeps releases moving. And if that wait turns out to be unnecessary tomorrow - great, we delete it. Until then, it's the tiny compromise that saves the day."}),s.jsx(Ee,{name:"Kosta Welke",title:"code monkey at Octomind",imageSrc:"/assets/blogs/kosta-welke-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function nR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"a programmer yelling at the clouds about vibe coding"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsxs("div",{className:"bg-octo-dark-light p-6 mb-8 border-l-4 border-white",children:[s.jsx("h4",{className:"text-xl font-bold text-white mb-2",children:"Vibe coding"}),s.jsx("p",{className:"text-octo-text text-lg mb-2",children:"noun"}),s.jsx("p",{className:"text-octo-text text-lg mb-2",children:"Writing computer code in a somewhat careless fashion, with AI assistance"}),s.jsxs("p",{className:"text-octo-text text-lg italic",children:["~"," ",s.jsx("a",{href:"https://www.merriam-webster.com/slang/vibe-coding",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Merriam Webster"})]})]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Vibe coding is all the rage these days, but is that really the way we want to go as an industry?"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["I'm neither the"," ",s.jsx("a",{href:"https://www.justfuckingcode.com/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"first"})," ","nor the"," ",s.jsx("a",{href:"https://antirez.com/news/153",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"second"})," ","guy to buck the trend but maybe my viewpoint from someone at a testing startup THAT USES AI is interesting to someone."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"State of the art LLM models"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["We all have heard the marketing spins of AI writing"," ",s.jsx("a",{href:"https://www.forbes.com/sites/jackkelly/2024/11/01/ai-code-and-the-future-of-software-engineers/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"25% of code at google"}),", or replacing"," ",s.jsx("a",{href:"https://x.com/bradmenezes/status/1927414638632735069",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"entire product departments"}),", but in my personal experience these models are just not"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"quite"})," there yet to be let loose completely unchecked."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["I can (and regularly do) let AI write some tiny prototypes or auto-complete my mock-data in tests. But to me I regularly run into road blocks whenever I ask the AI to"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"actually"})," help me on a task I am struggling with myself: After all, the auto-completion of boilerplate is nice, but this is not what is costing me most of my time in my day-to-day development work."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Just last week I was struggling with the old"," ",s.jsx("a",{href:"https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7dfcad3",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"'ESM hell'"})," ",`problem of an intricate "playwright being executed in a node subprocess" problem - no matter which LLM I tried to prompt, the answers weren't better than whatever I found on StackOverflow. And after all, that is completely understandable, the LLM is only working through its training corpus…`]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:[`And of course I admit, this is a problem only a handful of people on the planet probably have (maybe even no one else, I suspect it ALSO had to do with our way of linking the pnpm node_modules), but that's my point: The amount of time that is spent on the long tail of edge cases is absolutely not comparable with the "easy" cases of boilerplate: one costs me days, and one costs me a few seconds → I would`," ",s.jsx("strong",{className:"text-octo-purple-light",children:"LOVE"})," to use AI for the hard problems, but I just can't currently."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Reviewing AI code isn't fun"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Not only that AI doesn't solve my problems, but having models run loose on your codebase was the cause of this"," ",s.jsx("a",{href:"https://www.reddit.com/r/ExperiencedDevs/comments/1krttqo/my_new_hobby_watching_ai_slowly_drive_microsoft/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"hilarious reddit post"}),", where people pointed out some of the funny, but also sad, PRs that Github Copilot has opened on the .NET runtime repo, when let loose."]}),s.jsxs("figure",{className:"mb-6",children:[s.jsx("img",{src:"/assets/blogs/reddit-ai-code-comment.png",alt:"redditor comment on AI generated code",className:"w-full rounded-xl"}),s.jsxs("figcaption",{className:"text-octo-text text-sm mt-2 text-center",children:["source:"," ",s.jsx("a",{href:"https://www.reddit.com/r/ExperiencedDevs/comments/1krttqo/my_new_hobby_watching_ai_slowly_drive_microsoft/mtgbl3c/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Reddit"})]})]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["The comment encapsulates a lot of my feelings as well - if I need to _",s.jsx("em",{children:"actually"}),"_ review the AI code, I have to completely understand the problem, and due to the state of the art, much more than I would have to for my colleagues."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Check this this absolute insanity in a github comment chain:"}),s.jsxs("figure",{className:"mb-12",children:[s.jsx("img",{src:"/assets/blogs/github-copilot-hallucination.png",alt:"a github comment chain with copilot hallucination",className:"w-full rounded-xl"}),s.jsxs("figcaption",{className:"text-octo-text text-sm mt-2 text-center",children:["source:"," ",s.jsx("a",{href:"https://github.com/dotnet/runtime/pull/115733#issuecomment-2894165573",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"GitHub"})]})]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Testing your vibe coded mess"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["So how does it all relate to testing? Well I think that testing an AI models output with another AI without human interaction is a recipe for disaster. I think there's an argument to be made that QA should potentially be the ",s.jsx("strong",{className:"text-octo-purple-light",children:"last"})," thing that we replace with a fully autonomous agent."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:'If you think about it, the most important thing of an app is that it works "according to spec" of course. So if a fully AI-generated test is checking the AI-generated app, we end up in prime meme territory:'}),s.jsx("figure",{className:"mb-6",children:s.jsx("img",{src:"/assets/blogs/684a915940a1c87b9d956d69_Screenshot 2025-06-12 at 10.35.29.webp",alt:"3 spiderman meme mocking ai code generation / design / QA",className:"w-full rounded-xl"})}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["There ",s.jsx("strong",{className:"text-octo-purple-light",children:"MUST"})," be a human involved in checking the tests. It can of course be ai-assisted, but the final approval must lay with a human. I think this argument is kind of intuitive by itself but if it isn't the science itself agrees -"," ",s.jsx("a",{href:"https://en.wikipedia.org/wiki/Model_collapse",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"model collapse"})," ","is a real problem, that I think this also relates to: if AI is only trained / receives feedback from other AI, the aggregated error gets worse over time."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["Kind of how if you ask ai to recreate the same image you get"," ",s.jsx("a",{href:"https://www.reddit.com/r/ChatGPT/comments/1kawcng/i_went_with_recreate_the_image_as_closely_to/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"hilarious results"}),". I personally would NOT want my app to be tested only by another AI, but what about you?"]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"So, what now?"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["In our team at Octomind we have some people that are more bullish on the whole AI hype train than me and some that are less so. But overall we see AI as a very valuable tool, but only that. Something that"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"CAN"})," help you if used correctly and in the right place, and can also cause issues when letting it run rampant."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"You can be both excited about a technology and careful with putting it to use in a way that brings actual value. This is not a case of cognitive dissonance but a corrective to both extremes of the AI debate."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"—"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"If you like my thought process feel free to check out our app, it's free and you can see how we try to improve the e2e testing process with sprinkles of AI, good UI and visualizations and smart tricks like picking good / stable locators for you automatically."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"And If you think my take isn't nuanced enough, I'm missing something or just want to chat about tests, AI or general startup engineering, you can find me at:"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-2",children:[s.jsx("a",{href:"https://x.com/Germandrummer92",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"@Germandrummer92"})," ","on X"]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-2",children:[s.jsx("a",{href:"https://github.com/germandrummer92",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"@Germandrummer92"})," ","on github"]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["@daniel.draper on the"," ",s.jsx("a",{href:"https://discord.gg/3ShnZMKRfA",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"octomind discord"})]}),s.jsx(Ee,{name:"Daniel Draper",title:"lead octoneer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function rR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-4 leading-tight animate-fade-in-up",children:"AI testing: IDEs vs. testing platforms"}),s.jsx("p",{className:"text-xl text-octo-text mb-8 animate-fade-in-up",children:"When are you better off with an agentic IDE scripting e2e tests and when would you use an agentic testing platform?"}),s.jsx("figure",{className:"mb-12 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/682e07f36bb7cb6640ba36fd_9a2511d3c0595f0eb2c92660b3e0f9f0_cursor-vs-octo.webp",alt:"Two octopi facing off - representing Cursor vs Octomind",className:"w-full rounded-xl"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"We took our Octomind testing platform and ran some use experiments with devs writing & running end-to-end tests. We wanted to compare the 2 approaches to increase productivity in testing."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"We used Cursor as benchmark and had it generate Playwright tests, since Octomind uses Playwright test code under the hood."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Getting started: Tool setup simplicity vs. flexibility"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["The classic of the dev heavy vs. low-code dichotomy."," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," works with an effortless, one-click setup and off-the-shelf support for test setup (environments, variables, etc.) Simply provide your web app's URL, and the platform is ready to roll. It's ideal for teams who value speed and ease, letting you jump straight into testing without worrying about detailed configuration."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["In contrast, ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"})," offers traditional software project flexibility - great if you're an experienced developer comfortable with version control, node.js setup, and IDE configurations. This freedom is beneficial, yet it's important to note that setup requires more upfront effort and knowledge."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Composing test cases: Intuition vs. precision"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Crafting test cases in ",s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," feels intuitive for users of different skill levels - describe your scenarios using natural language, and let the AI agent handle the rest. The test recorder is similarly frictionless. You don't need in-depth coding knowledge, it's decently accessible for teams beyond software developers."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"}),", on the other hand, uses AI to assist you in generating Playwright tests directly within your IDE. While powerful, Cursor's effectiveness heavily depends on your domain knowledge and familiarity with coding tests. This offers precision, but less experienced users might face steep learning curves."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Debugging: Visual insight vs. code analysis"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," provides an integrated recorder and visual step editor to fix tests. Debugging is a guided and mostly visual experience and - you quickly see and correct exactly where tests deviate."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["Debugging in ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"})," requires a deeper dive. With no immediate visual aids, you must run tests, monitor execution, identify issues, and manually adjust code. It's precise, but might be straining until you identify the problem depending on your coding skill."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Flexibility and freedom: Structured ease vs. infinite possibilities"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," offers a comprehensive yet structured environment built atop Playwright. While it covers most testing scenarios - including complex ones like OTP and 2FA flows - it inherently restricts you to its features and integrations."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"})," allows for the opposite - unlimited freedom. You're free to leverage any Playwright capability, ideal if your team thrives on customization and has expert knowledge. However, such liberty demands significant testing experience."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Test structure & management: Organized vs. manual management"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," provides descriptive prompts, structured steps, screenshots, and built-in management capabilities (folders, tags, AI-driven searches). It's an out-of the box, yet prescriptive approach."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["In ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"}),", structure depends entirely on your team's organizational skill. While it can provide incredibly tailored code structures, poorly managed tests can quickly become overwhelming, especially as your suite scales."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Execution: Built-in vs. DIY"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," comes with a built-in test runner. Execution is seamless - everything from environments to CI/CD integrations and scheduling is automated. It also handles complex issues like nuxt hydration, shared authentication, or geo-based proxies which can become painful when DIYed very quickly."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["To be fair to ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"})," - it is an IDE - not an end-to-end testing platform. It doesn't inherently handle execution logistics - it's not what it's built for. The responsibility for configuring CI/CD, environment management, parallel test execution, and other advanced setups lies entirely with your team, demanding substantial testing expertise."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Maintenance: Guided assistance vs. code debugging"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["To keep the entire testing workload in check, ",s.jsx("strong",{className:"text-octo-purple-light",children:"Octomind"})," ","offers features for easy maintenance. It identifies root causes of failures, visually compares them to successful runs, and offers auto-maintenance solutions. It can, because it has built and run the tests."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:["Maintenance with ",s.jsx("strong",{className:"text-octo-purple-light",children:"Cursor"})," will lead to manually inspecting and debugging tests. Although the AI can assist since it has access to your codebase, once you get into resolving complex issues, manual effort and advanced debugging skills are needed."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Verdict: When to use which?"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This comparison might seem like a stretch - Octomind and Cursor are 2 very different AI-assisted tools. However, their application area overlaps - they are both used in the app testing process. We discuss this often with our users who are also fervent users of AI IDEs. That's why I decided to write this summary."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Choose Octomind / testing platform"})," if your goal is a complete, streamlined testing solution that significantly reduces effort and allows broader team participation. Its built-in intelligence and intuitive maintenance workflows make it perfect for teams prioritizing productivity and ease of use."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Choose Cursor / AI-powered IDE"})," if your priority is flexibility, customization, and absolute control over your testing processes. It's perfect for seasoned developers comfortable navigating advanced test setups, deep debugging, and manual management. You would also need to invest a decent amount of time to set up the infrastructure for test execution and sort out advanced testing use cases like 2FA or email flows which come off the shelf in Octomind."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"In the end, your decision depends on your preference - each tool caters distinctly to different team profiles and testing philosophies."}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder & CPO",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}function oR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-8 leading-tight animate-fade-in-up",children:"my MCP vision for Octomind"}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"In Feb 2022, I changed my LinkedIn to display my vision of the future:"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6 italic",children:"Coding will be a technical discussion between humans & computers. Sooner than we think!"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"A few months ago, I had to delete the second sentence: Sooner than we think. Because that time is now. Coding will be a technical discussion between humans & computers. Octomind makes sure it is well-tested!"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"As the only quasi-scalable replicator we have, code will be the key to solving many of humanity's biggest problems. But only if the code is of high quality and works according to requirements at all times and during all usage scenarios. To guarantee that, we built Octomind."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Friday was a big step in that direction: we listened to your feedback and open-sourced our MCP (Model Context Protocol) implementation so you can start using Octomind from your preferred agent environment: Cursor, Windsurf, VSCode, Claude Desktop and soon other agents of your choice without leaving your preferred interface and workflows. Our goal is to be a good citizen and integrate with your existing workflows."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Especially since last week's historic handshake between openAI & anthropic it became clear that MCP is the protocol on which agentic services will cooperate. This cooperation requires safe dissemination of knowledge/context and while MCP still has a few shortcomings regarding authentication & authorization etc., it is on the right trajectory. To address it, major security companies like Cloudflare are allocating >4 of their most senior colleagues to MCP full-time (as of March 2025). Notion, Figma and other scaleups are investing in similar headcounts. We would even go so far and postulate:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 my-8",children:s.jsx("p",{className:"text-octo-teal text-xl italic",children:"Every software company must clearly establish its position regarding MCP."})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"So what's MCP? It's basically a context or knowledge exchange protocol. Context you have in one app is re-usable and almost 1:1 transferable (might need to be pseudonymized or compressed into a vector) to other MCP-compliant apps. With MCP you are breaking down knowledge silos and it enables you to bridge semantic gaps that the AI beforehands was not able to cross. We saw it in our customers' AI accuracy: for common-knowledge apps that are consumer-facing like stock trading apps, most AI approaches will have a pretty good understanding. But just within fintech, the domain expertise required can get infinitely complex: the same AI will struggle with high-frequency trading apps that are used by less than 100 expert traders. MCP has the potential to overcome this and give domain expertise to the very same AI that previously struggled with semantic understanding of the app at hand. It can act as a domain expert that teaches & onboards the AI to these difficult to grasp semantic gaps in AI understanding. The best thing about it: this happens in your preferred apps like Cursor, Windsurf and VScode for developers and for product developers: Claude Desktop and soon chatGPT."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["This seamless blend of Cursor and Octomind opens up a whole new dimension of possibilities and use cases. On"," ",s.jsx("a",{href:"https://octomind.dev/product/mcp",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"octomind.dev/product/mcp"}),", we present 3 of these:"]}),s.jsxs("ol",{className:"text-octo-text text-lg leading-relaxed mb-6 list-decimal list-inside space-y-2",children:[s.jsx("li",{children:"Code to test"}),s.jsx("li",{children:"Anything to test"}),s.jsx("li",{children:"Test report and analyses to your preferred app"})]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"I think that most of our technical users will use Octomind to generate and execute tests primarily through the Cursor and Windsurf MCP. It's our preferred way of using it, too :)"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Last week's open-sourcing of our"," ",s.jsx("a",{href:"https://github.com/OctoMind-dev/octomind-mcp",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"MCP implementation"})," ","is our contribution to the ecosystem. Watch"," ",s.jsx("a",{href:"https://www.youtube.com/watch?v=71zh5vWwfb4",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"this video"})," ","to see it in action."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"Let's together turn testing from afterthought into a first-class citizen. Let's together repair the damage done by vibe coding!"}),s.jsx(Ee,{name:"Marc Mengler",title:"Co-Founder & CEO at Octomind",imageSrc:"/assets/blogs/marc-mengler-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const aR=()=>s.jsxs("div",{className:"min-h-screen bg-[#13142B]",children:[s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-3xl",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-12",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-4",children:"test isolation is (not) fun"}),s.jsx("p",{className:"text-xl text-gray-400",children:"The way I see the problem of isolation in end-to-end testing"})]}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text mb-6",children:"Writing a single test is fun. Writing a test suite that scales is… sometimes less fun."}),s.jsx("p",{className:"text-octo-text mb-6",children:"The more tests you write the less it becomes a matter of individual tests, and more a matter of designing the whole testing system. There are countless decisions to be made."}),s.jsx("p",{className:"text-octo-text mb-6",children:"The bad news is that even if you are a well seasoned tester, practices from one company may not be directly applicable to another company. Every application is different and therefore each requires a different strategy. There really is no best practice."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/67d0102653d4a54034e7811d_best-practice-meme.png",alt:"Best practice meme",className:"rounded-lg w-full"}),s.jsxs("figcaption",{className:"text-sm text-gray-500 mt-2",children:["source:"," ",s.jsx("a",{href:"https://x.com/chantastic/status/1440770690559340548",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"X"})]})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"The good news is, that there are at least some areas, with challenges that many have been through before and ones we can learn from. In this blogpost, I'd like to discuss one particular category of these challenges - test isolation."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Browser isolation"}),s.jsxs("p",{children:["There are many test automation tools that offer automatic script generation. Playwright has"," ",s.jsx("a",{href:"https://playwright.dev/docs/codegen",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"codegen features"}),", Cypress has its"," ",s.jsx("a",{href:"https://docs.cypress.io/app/guides/cypress-studio",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Studio"}),", Selenium"," ",s.jsx("a",{href:"https://www.selenium.dev/selenium-ide/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"offers a full IDE"}),". There are even whole products based on the idea of recording and replaying tests."]}),s.jsx("p",{className:"text-octo-text mb-6",children:"While these tools are fun to use and can be great for learning the basics, they quickly fall short, when it comes to the repeated use of code that they generated."}),s.jsx("p",{className:"text-octo-text mb-6",children:"Even just re-running a newly recorded test can be a problem, because the data created in the first recording may get in the way of the second run."}),s.jsx("p",{className:"text-octo-text mb-6",children:"But it's not just about data. Modern websites these days use cookies, local storage, indexed databases and other forms of local in-browser storage that provides important context to the application's frontend code, or servers."}),s.jsx("p",{className:"text-octo-text mb-6",children:"The simplest example of this are personalized cookie settings. These settings are usually saved in (you guessed it) cookies, but once a user opens a new browser or enters incognito mode, these settings are gone."}),s.jsx("p",{className:"text-octo-text mb-6",children:"This same problem then translates to test automation. Usually, testing frameworks clean up the state of the browser to avoid polluting the context of individual tests. But as the mentioned example shows, there are many situations where this might actually become a problem."}),s.jsx("div",{className:"my-6 rounded-xl overflow-hidden bg-[#0d0d1a] border border-white/10",children:s.jsx("pre",{className:"p-4 overflow-x-auto text-sm",style:{fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#8E8EE7"},children:"describe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Cookie Consent Tests'"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[", () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'new user sees cookie banner'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"goto"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'https://yourpage.com'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#6b7280"},children:[" ","// A new user should see the cookie consent banner"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" banner = "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"locator"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'.cookie-banner'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"expect"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" banner."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"isVisible"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"())."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"toBe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"true"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'returning user does not see banner'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsxs("span",{style:{color:"#6b7280"},children:[" ","// First visit to set the cookie"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"goto"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'https://yourpage.com/'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"click"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'.cookie-banner-accept'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,` `,s.jsxs("span",{style:{color:"#6b7280"},children:[" ","// Simulate returning to the site"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"reload"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"();"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" banner = "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"locator"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'.cookie-banner'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"expect"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" banner."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"isVisible"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"())."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"toBe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"false"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:["}",");"]})]})})}),s.jsx("p",{children:"In practice this means that there are two different perspectives or user flows that need to be tested in order to get a good coverage:"}),s.jsxs("ol",{className:"list-decimal list-inside space-y-2 my-4",children:[s.jsx("li",{children:"brand new user experience"}),s.jsx("li",{children:"the experience of returning users"})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"There's a tendency to think in user scenarios when creating test automation, but even the same scenario can have different outcomes given a difference in context."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Test isolation"}),s.jsx("p",{children:"When it comes to test isolation, I feel like it's one of those principles that every test automation beginner learns about. A test should not interfere with any other tests. Simple as that."}),s.jsxs("p",{className:"text-octo-text mb-6",children:["But then when it comes to reality, it's much,"," ",s.jsx("strong",{children:s.jsx("em",{className:"text-octo-teal",children:"much"})})," ","harder to stick to this. Let's take a simple example where we want to test a to-do app. We want to:"]}),s.jsxs("ol",{className:"list-decimal list-inside space-y-2 my-4",children:[s.jsx("li",{children:"create an item"}),s.jsx("li",{children:"delete an item"})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"In principle, these should of course be two separate tests. But you can see how tempting it is to merge them together to speed up the execution. Because after all, if we really want to isolate the second test, we'll need to create an item anyway."}),s.jsx("div",{className:"my-6 rounded-xl overflow-hidden bg-[#0d0d1a] border border-white/10",children:s.jsx("pre",{className:"p-4 overflow-x-auto text-sm",style:{fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:"// tests depend on each other"}),` `,s.jsx("span",{style:{color:"#8E8EE7"},children:"describe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Todo List'"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[", () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'create todo item'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"createTodo"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Buy milk'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'delete todo item'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"deleteTodo"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Buy milk'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:["}",");"]}),` `,` `,s.jsx("span",{style:{color:"#6b7280"},children:"// each test is independent"}),` `,s.jsx("span",{style:{color:"#8E8EE7"},children:"describe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Todo List'"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[", () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'can create new todo item'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" todoText = "}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Buy milk'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:";"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"createTodo"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"(todoText);"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" newItem = "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"locator"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'.todo-item'"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[", ","{"," hasText: todoText ","}",");"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"expect"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" newItem."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"isVisible"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"())."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"toBe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"true"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"test"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'can delete existing todo item'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:", "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" () => ","{"]}),` `,s.jsxs("span",{style:{color:"#6b7280"},children:[" ","// Setup: Create item specifically for this test"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" todoText = "}),s.jsx("span",{style:{color:"#15D7AB"},children:"'Delete me'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:";"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"createTodo"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"(todoText);"}),` `,` `,s.jsxs("span",{style:{color:"#6b7280"},children:[" ","// Actual deletion test"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"deleteTodo"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"(todoText);"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" deletedItem = "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" page."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"locator"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"'.todo-item'"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[", ","{"," hasText: todoText ","}",");"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"expect"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" deletedItem."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"isVisible"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"())."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"toBe"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"("}),s.jsx("span",{style:{color:"#15D7AB"},children:"false"}),s.jsx("span",{style:{color:"#C3C9D3"},children:");"}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","}",");"]}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:["}",");"]})]})})}),s.jsxs("p",{className:"text-octo-text mb-6",children:[s.jsx("p",{children:"In real-life scenarios, we of course deal with much more complex scenarios, but the decisions that need to be made are similar in principle. When deciding on whether to merge tests together, create dependencies or to fully isolate them, I personally always side with isolation."}),s.jsx("p",{className:"text-octo-text mb-6",children:"This is mostly because I want to be able to run tests in parallel. While full test isolation might create a slight increase in execution time, parallelization will decrease it exponentially."}),s.jsx("p",{className:"text-octo-text mb-6",children:"It is good to consider parallelization from day one of creating test automation. It's much more complicated to achieve it if tests are not properly isolated. But what is proper isolation?"})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"Consider testing an arbitrary SaaS platform - The most basic entity that decides how the page looks would usually be a single user account. So for a fully parallel test execution, each parallel process must run as a different user. In that case, when running 10 parallel processes, we must create 10 testing accounts."}),s.jsxs("p",{className:"text-octo-text mb-6",children:['In the case of an e-commerce application things might get a bit more complicated, because even if we create separate customer accounts, we still need to deal with available items in the store. In case they run out, the test automation would unexpectedly fail. This is a situation in which the basic entity would be the store itself. Before test execution, data of the whole store must be ready to "fulfil orders" of the whole test run. This of course might get pretty complicated, but it is a'," ",s.jsx(Fs,{to:"/blog/testing-is-more-about-setup-than-scripts",className:"underline",children:"huge part of creating good test automation"}),"."]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Data isolation"}),s.jsxs("p",{children:["When dealing with test automation, data is being thrown around everywhere. That's what"," ",s.jsx("em",{className:"text-octo-teal",children:"should"})," happen. When your test automation resembles the way your app is going to be used, it will create, modify and delete data during the process. The main question is how to design a test suite in such a way that this data movement does not become a problem for both your system nor your test stability. These are some of the concerns, just to name a few:"]}),s.jsxs("ul",{className:"list-disc list-inside space-y-2 my-4",children:[s.jsx("li",{children:"creating user accounts with proper permissions"}),s.jsx("li",{children:"creating user data"}),s.jsx("li",{children:"preparing the environment for the system under tests"}),s.jsx("li",{children:"preparing assets, e.g. images or pdf documents to use during test execution"}),s.jsx("li",{children:"cleaning up data and data teardown"}),s.jsx("li",{children:"isolating data between individual tests"}),s.jsx("li",{children:"isolating data between sequential test runs of the same test"}),s.jsx("li",{children:"isolating data between parallel runs of the same test"})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"The data isolation problem ties all of the previous problems together. While it might be tempting to reuse existing data across tests, this approach can become a hellish nightmare once tests run in parallel. Whenever possible, I'd advise for as much data isolation as possible."}),s.jsxs("p",{className:"text-octo-text mb-6",children:["One of the common approaches is to create a ",s.jsx("strong",{children:"data factory pattern"})," that generates isolated data for each test. For example, when testing a user profile feature, rather than sharing a single test account, your data factory might look something like this:"]}),s.jsx("div",{className:"my-6 rounded-xl overflow-hidden bg-[#0d0d1a] border border-white/10",children:s.jsx("pre",{className:"p-4 overflow-x-auto text-sm",style:{fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" createTestUser = "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"async"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" (prefix: "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"string"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" = "}),s.jsx("span",{style:{color:"#15D7AB"},children:"''"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"): "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"Promise"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" => ","{"]}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"const"}),s.jsxs("span",{style:{color:"#C3C9D3"},children:[" user = ","{"]}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","username: "]}),s.jsx("span",{style:{color:"#15D7AB"},children:"`test_user_${prefix}_${Date.now()}`"}),s.jsx("span",{style:{color:"#C3C9D3"},children:","}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","email: "]}),s.jsx("span",{style:{color:"#15D7AB"},children:"`test_${prefix}_${Date.now()}@example.com`"}),s.jsx("span",{style:{color:"#C3C9D3"},children:","}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","preferences: ","{"]}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","theme: "]}),s.jsx("span",{style:{color:"#15D7AB"},children:"'light'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:","}),` `,s.jsxs("span",{style:{color:"#C3C9D3"},children:[" ","notifications: "]}),s.jsx("span",{style:{color:"#15D7AB"},children:"'enabled'"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" }"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" };"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"await"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" backend."}),s.jsx("span",{style:{color:"#8E8EE7"},children:"createUser"}),s.jsx("span",{style:{color:"#C3C9D3"},children:"(user);"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:" "}),s.jsx("span",{style:{color:"#8E8EE7"},children:"return"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" user;"}),` `,s.jsx("span",{style:{color:"#C3C9D3"},children:"};"})]})})}),s.jsxs("p",{className:"text-octo-text mb-6",children:["This approach ensures that each test has its own unique data set, eliminating potential conflicts between parallel test runs. However, when debugging, it's beneficial to have a way to track data that was created. The example above randomizes the user name and email using"," ",s.jsx("code",{className:"bg-[#1a1b2e] px-2 py-0.5 rounded text-octo-teal font-mono text-sm italic",children:"Date.now()"}),", but in case of debugging they might be a bit hard to find. An effective way of solving this is to produce an output of a test run that stores these names."]}),s.jsx("p",{className:"text-octo-text mb-6",children:"While creating isolated data is important, cleaning up that data is equally crucial. Test data can accumulate and cause various problems. If your tests run highly parallel and on every code change, databases quickly fill up, slowing down the system under test and potentially even leading to increasing costs."}),s.jsx("p",{className:"text-octo-text mb-6",children:"There are often discussions on whether a data cleanup should happen before test execution starts, or after it finishes. I'd argue for doing it before the test execution, ideally completely isolated from the test script itself, as this means you can still access the test environment when debugging after a specific test run and ensure that a potentially failed teardown from before won't affect your next test run."}),s.jsx("p",{className:"text-octo-text mb-6",children:"All things considered, data isolation strategies should evolve with your test suite. It's valuable to plan ahead, but it's pretty much impossible to plan everything as the application under test evolves. Premature optimization can be a costly endeavor, with questionable results, so your test suite and isolation maturity should grow together with your product needs."}),s.jsx("hr",{className:"my-8 border-gray-700"}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"How do we isolate our tests?"}),s.jsx("p",{children:'At Octomind, we ensure test isolation by a variation of the test factory pattern from before. The core idea is that every test run generates new, unique data - each entity (such as documents, users, or transactions) is created with distinct identifiers and names. This means that even if multiple tests rely on an operation like "create a new document," each test instance will produce an independent document when executed, preventing unintended dependencies or data collisions.'}),s.jsx("p",{className:"text-octo-text mb-6",children:"This approach enables parallel execution since tests are not competing for the same resources, significantly reducing overall test runtime. Moreover, it makes running tests against multiple environments seamless, as each test case remains self-contained and does not introduce cross-environment conflicts."}),s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]});function iR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-teal/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-4xl mx-auto px-4 md:px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold text-octo-teal mb-4 leading-tight",children:"AI doesn't belong in test runtime"}),s.jsx("p",{className:"animate-fade-in-up text-xl text-octo-text mb-12",children:"Not all AI in e2e testing is created equal"}),s.jsxs("div",{className:"animate-fade-in-up prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Adopting generative AI in end-to-end testing improves test coverage and reduces time spent on testing. At last, automating all those manual test cases seems within reach, right?"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"We have to talk about the stability and reliability of AI in this context, too. The concerns are real and I'd like to address a few here."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Testing tools don't use AI in the same way"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"AI testing tools use AI differently to write, execute and maintain automated tests. The LLMs under the hood are not deployed in the same place in the same way by every testing technology. The AI can be deployed as:"}),s.jsxs("ol",{className:"text-octo-text text-lg leading-relaxed mb-12 list-decimal list-inside space-y-3",children:[s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"AI used in runtime:"})," LLM goes into the app and interacts with it every time a test or a test step is executed."]}),s.jsxs("li",{children:[s.jsx("em",{className:"text-octo-teal",children:"deterministic code used in runtime:"})," LLMs are used to create interaction representations that translate into deterministic code used during test execution."]})]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"What can go wrong?"}),s.jsxs("figure",{className:"mb-6",children:[s.jsx("img",{src:"/assets/blogs/ai-runtime-reddit-comment.png",alt:"Reddit comment about AI in testing",className:"mx-auto rounded-xl"}),s.jsxs("figcaption",{className:"text-octo-text/70 text-sm mt-2 text-center",children:["source:"," ",s.jsx("a",{href:"https://www.reddit.com/r/webdev/comments/1i3ictz/comment/m7n2pam/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Reddit"})]})]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"The good news is that all of the concerns can be mitigated by adopting the right architecture in the right place of testing cycle."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Use AI for test creation"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"AI is most valuable during the creation and maintenance phase of test cases. Let's take a scripting example. You could begin with a prompt describing your desired test case and allow the AI to generate an initial version."}),s.jsxs("figure",{className:"mb-6",children:[s.jsx("img",{src:"/assets/blogs/ai-runtime-cursor-screenshot.png",alt:"Cursor screenshot showing prompt and output",className:"mx-auto rounded-xl"}),s.jsx("figcaption",{className:"text-octo-text/70 text-sm mt-2 text-center",children:"Cursor screenshot — prompt + output"})]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"If you're lucky, the AI may produce a valid and ready-to-use test case right away. How convenient!"}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["If the AI struggles to interpret your application,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"you - the domain expert"})," - can step in and guide it, ensuring that the resulting test case is accurate and robust. It's a good practice to keep AI fallibility on top of your mind when you're accessing its output. It's an even better practice for tool developers to build the reminder into the process."]}),s.jsxs("figure",{className:"mb-12",children:[s.jsx("img",{src:"/assets/blogs/ai-runtime-agent-help.png",alt:"AI agent asking for help",className:"mx-auto rounded-xl"}),s.jsx("figcaption",{className:"text-octo-text/70 text-sm mt-2 text-center",children:"AI agent asking for help"})]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Do not use AI in test runtime"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Ideally, AI should not be used during runtime. It's slow. It's brittle. It's costly. A test case represents an expectation of how a system should work in a particular area. The agentic AI must try to fulfill this expectation. No workarounds. Only if the expectation is formulated precisely enough (code / steps) it can be validated against."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"I suggest relying on established automation frameworks such as Playwright, Cypress, or Selenium for test execution. By using standard automation framework code, your test cases can remain deterministic, allowing you to execute them fast and reliably. Some providers even offer execution platforms to scale your standard framework test suites efficiently."}),s.jsx("figure",{className:"mb-12",children:s.jsx("img",{src:"/assets/blogs/ai-runtime-flow-diagram.png",alt:"Flow diagram showing AI in test lifecycle",className:"mx-auto rounded-xl"})}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Use AI for test maintenance"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"The case for using AI in test auto-healing is quite strong. When given boundaries and the 'good example' of the original test case, the AI worst instincts can be mitigated. The idea is that AI generation works best when the problem space is limited."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-12",children:"A robust solution to auto-maintenance would address a huge pain point in end-to-end testing. Maintenance is more time consuming (and frustrating) than scripting and running tests combined. There are many tools building auto-maintenance features using AI right now. If good enough, they could considerably simplify the process of keeping your tests up-to-date and relevant."}),s.jsx(Ee,{name:"Daniel Roedler",title:"Chief Product Officer and Co-founder",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const lR=()=>s.jsxs("div",{className:"min-h-screen bg-[#13142B]",children:[s.jsx(te,{}),s.jsx("article",{className:"pt-32 pb-16",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-3xl",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-12",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-4",children:"testing is more about setup than scripts"}),s.jsx("p",{className:"text-xl text-gray-400",children:"AI assisted testing is no different"})]}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text mb-6",children:"Comparing testing frameworks is the type of news that gets a lot of eyeballs online. Playwright vs. Cypress vs. Selenium vs. Webdriver.io - everyone is interested in seeing which one is better, faster, more stable and easier to work with."}),s.jsx("p",{className:"text-octo-text mb-6",children:"Speed and performance is extremely scrutinised. It seems like everyone is migrating from Cypress to Playwright, because it offers faster test execution. But is that really a goal worth pursuing?"}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/test-setup-meme-1.jpg",alt:"Meme about test execution speed",className:"rounded-lg mx-auto"})}),s.jsx("p",{className:"text-octo-text mb-6",children:"It seems like a no-brainer to switch to the faster framework. If the test execution is faster, why wouldn't you make the switch?"}),s.jsx("p",{className:"text-octo-text mb-6",children:"The problem becomes more apparent once you understand the whole system of test automation. Test automation is never just about script execution. Writing, maintaining, setting up, retrying and a number of other concerns are what makes up the daily life of a test automation engineer. A good test automation flow is the one where all of these parts of the process are fast, not just the test execution."}),s.jsx("p",{className:"text-octo-text mb-6",children:"If framework migration evangelists were honest, they would include time for setup into consideration. I know that edges on a long term experiment, so no blame here. Test scripting is not the biggest time investment. Set-up is."}),s.jsx("p",{className:"text-octo-text mb-6 italic",children:"* I'll pretend maintenance isn't a thing for now. That deserves its own article."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Setting up test environments"}),s.jsx("p",{className:"text-octo-text mb-6",children:"When it comes to testing, a big part of what makes a project go slow (or fast) is the system under test. Testing tools are getting faster these days, and it seems that we are no longer limited by the speed of testing tools, but rather by applications under test."}),s.jsx("p",{className:"text-octo-text mb-6",children:"Clicking, typing and interacting with the application under test is a task that most of the testing frameworks can handle just fine. Any seasoned test automation engineer will probably tell you that the real complexity is not in these interactions, but in all things around - abstractions, system design, data seeding, environment concerns, authentication setup just to name a few. All of these things are what makes testing possible and make up the real work of test automation."}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/test-setup-linkedin-quote.png",alt:"Quote from LinkedIn by Filip Hric",className:"rounded-lg mx-auto"})}),s.jsx("p",{className:"text-octo-text mb-6",children:"So what are the main concerns when it comes to setting up a well performing test automation? There are a couple of them. You can think of your test automation script as of a real user. Simply starting a user journey might require answers to following questions:"}),s.jsxs("ul",{className:"list-disc list-inside space-y-2 my-4 text-octo-text",children:[s.jsx("li",{children:"What does the user need in order to interact with the system?"}),s.jsx("li",{children:"Are there any limitations in when/how can user interact, such as authorization, authentication, payment or other?"}),s.jsx("li",{children:"What kind of data is assumed when interacting with the system? (e.g. compare e-commerce vs. internet banking apps)"}),s.jsx("li",{children:"Are there any integrations to third party systems that the user needs to have first?"}),s.jsx("li",{children:"Does a user interact with other users when using the system?"}),s.jsx("li",{children:"How do you make sure you don't pollute your production analytics with test data?"})]}),s.jsx("p",{className:"text-octo-text mb-6",children:"There are, of course, many questions like these that will shape how the test script will look like, or what kind of setup will need to be done before it runs. In order to create a good testing system, these problems require well thought-through solutions. The biggest challenge of testing is that there's no solution that would be 100% transferable to other projects."}),s.jsx("p",{className:"text-octo-text mb-6",children:"This brings me back to test framework comparisons. A very big part of test automation is actually not test automation, but preparation for it. A good test automation project is more than just a good script, it's a good overall experience. After all, test automation serves the goal of shipping faster, with greater confidence."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Test automation in the AI era"}),s.jsx("p",{className:"text-octo-text mb-6",children:"The AI wave has influenced test automation as well. It seems that there are multiple companies that now tackle test automation in new, innovative ways. Autonomous testing is on the rise, and bold claims on how testing is going to be done purely by AI are made."}),s.jsx("p",{className:"text-octo-text mb-6",children:"But many of these seem to take one part of test automation and execute it well, while forgetting others. Creating an automation script is a task that many autonomous companies jump on to, and have been able to achieve at varying levels of success. Demos of these tools can be quite impressive, but much like the reviews I mentioned earlier, they cover only part of the ground."}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/6799f7f2a14c6099642e6adf_9hs99q.jpg",alt:"Meme about AI testing",className:"rounded-lg mx-auto"})}),s.jsx("p",{className:"text-octo-text mb-6",children:"Test automation was never just the problem of creating scripts, but as we established earlier, it's also a challenge of proper setup and proper context. Autonomous testing solutions need to look at the whole system of test automation related problems and be able to go beyond simple scripting."}),s.jsx("p",{className:"text-octo-text mb-6",children:"Much like being able to prompt LLM through chatGPT or Claude, autonomous testing services need to have a way to provide proper context for the environment under test. Not only the URL of the application under test, but data seeds, environment variables and other settings are what tackles the test automation as a whole, rather than just part of it."}),s.jsx("hr",{className:"border-white/20 my-8"}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"AI testing tool supporting setup"}),s.jsx("p",{className:"text-octo-text mb-6",children:"Setup is a critical challenge. At Octomind, we've built a set of features to help you put up testing and run it quickly. We will be expanding these features as we go."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-4",children:"Test portability"}),s.jsxs("p",{className:"text-octo-text mb-6",children:[s.jsx("a",{href:"https://octomind.dev/docs/account-view/environments",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Environments"})," ","are essential for running the same test suite across different stages of deployment. Staging, canary, production - you can create as many environments you need. Login credentials can be adjusted per environment and you can define different authentication methods for each stage."]}),s.jsxs("p",{className:"text-octo-text mb-6",children:["You can easily define"," ",s.jsx("a",{href:"https://octomind.dev/docs/advanced/variables",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"custom variables"})," ","and incorporate them into your test cases. They are created in the default environment ensuring they appear consistently across all other environments. They allows you to assign different values to variables depending on the environment, making it easier to maintain test cases across multiple setups."]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-4",children:"Test repeatability"}),s.jsxs("p",{className:"text-octo-text mb-6",children:["To maintain a consistent test environment for each run,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"setup and teardown"})," strategies are essential. They improve test repeatability and optimize execution time. For example, if a specific element - such as a support ticket in a ticketing system - can be assumed to exist, you can immediately interact with it instead of creating it from scratch for every test."]}),s.jsx("p",{className:"text-octo-text mb-6",children:"We are exploring several options to facilitate setup and teardown at the moment. We will keep you posted once this is shipped."})]}),s.jsx("div",{className:"mt-16 pt-8 border-t border-white/10",children:s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"})}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),cR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsxs("article",{className:"relative z-10 max-w-4xl mx-auto px-4 pt-32 pb-16",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-12",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"stop automating manual test cases"}),s.jsx("p",{className:"text-xl text-muted-foreground",children:"There's more to test automation than simply creating scripts automating tests you would execute manually."})]}),s.jsx("div",{className:"mb-12 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/6787b46c51d6640a65c6baa8_automation-octo.webp",alt:"Octopus automation concept",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Those that have been watching the testing landscape for a while might remember the craze that occurred when test automation started going mainstream. Will testers lose their jobs? What happens when we automate everything?"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Looking back, those concerns sound almost funny. Years of test automation have shown that despite significant speed improvements, companies still combine manual QAs with test automation engineers to help shipping high quality products."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["The recent ",s.jsx("strong",{className:"text-octo-purple-light",children:"'shifting left'"})," trend - pushing testing and quality processes to the earlier stages of development - has increased the focus on test automation. Many companies insist on transforming former manual QA teams to test automation and equip everyone with automation skills. The goal is to automate everything that was previously done manually. While there's a good argument for broadening the technical skills of QA teams,"," ",s.jsx("em",{className:"text-octo-teal",children:"there's definitely more to test automation than simply creating scripts to automate test cases."})]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Automating test cases"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Let me state it clearly. Automating manual test cases 1:1 is a bad idea."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This idea usually stems from the desire to keep the same test coverage as when tests are performed manually. Simply put, the idea is:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-4 italic mb-6",children:s.jsx("strong",{className:"text-octo-purple-light",children:"One test case = one test script"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"But this does not meet with the reality of what test automation really is. Manual checks are not only a series of steps but a series of various qualitative assertions such as visual look of the application under test, exploratory side quests, prompting developers for additional contents and little experiments. Any tester worth their salt will not mindlessly perform a series of test steps and report back only when they're unable to perform the next one. That's what makes it more valuable than a test script. On the other side, there is much value in test automation that is worth pursuing."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Test automation makes QA much faster and also more reliable. You can run test automation at any time and more frequently. Since it is automated you can also include the less important tests with any issues and this way gain more confidence. It's also a lot cheaper and can run outside working hours."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"It requires a different approach than simply scripting scenarios though."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"How to automate tests"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Whenever a tester runs a manual check, they log in and have a broad goal of what needs to be done. There are no broad goals when it comes to test automation. A testing script needs a precise goal, precise result and precise set of steps. Without those, an end-to-end test will become overly complex, with way too many conditions altering the end result."}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-manual-vs-automated.png",alt:"Manual vs automated testing visual concept",className:"max-w-xl rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This means that test automation needs to be more streamlined. It's typically a good practice to follow some kind of pattern, so that there's a standard to be followed for the whole test automation team. One of the good ones is the AAA pattern, which follows a set structure:"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-aaa-pattern.png",alt:"Arrange - Act - Assert - 3 phase concept of test automation",className:"mx-auto rounded-lg w-40"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This structure divides a test automation script into three distinct parts, each fulfilling a different objective."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Arrange"})," is a test preparation phase. It has the goal of making sure that the data is properly seeded, the application under test is in a proper state and the context is clearly set. Typically, the ",s.jsx("em",{className:"text-octo-teal",children:"arrange"})," phase can be something like a login, creating some test data or simply opening the proper subpage."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Act"})," is the heart of the automation script. It's a series of steps, closest to what the test case might be. This phase is responsible for getting the application from state A to state B. The series of steps are implicitly asserting that the functionality of the application under test is unbroken and allows the user to use the app's functionality. In a to do app, this would be creating, editing or deleting a to-do item."]}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Assert"})," is the most important step of a test. Without an assertion, the script is merely a series of steps. Assertions are an explicit confirmation that the functionality of the application under test works. In a to-do app, assertion might make sure that a to-do item is visible after it was created, no longer visible after it was deleted and so on."]}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Manual vs. automated"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"You could probably see some resemblance of the AAA pattern in manual testing. Even in manual testing there's preparation, exploring and assessment. But as we stated earlier, that does not mean that simply automating a test case is going to work well."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Let's demonstrate this in an example. The following is a cookie message that appears on a page:"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-cookie-banner.png",alt:"Cookie banner conceptual visual",className:"max-w-md w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:'This is a message that can appear seemingly randomly on a page. If testing this message is not within the scope, then the best course of action is to simply click "accept all" when the cookie consent message appears and move on.'}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"In test automation this poses a challenge. The message might be covering a portion of the page and we need to get it out of the way when it appears. Test automation scripts usually run in a clean browser which means that we will see this message more often than when testing manually. This might be aided with a condition in the test script. In pseudo code it might look like this:"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-test-script.png",alt:"Test script condition for a cookie test",className:"max-w-md w-full rounded-lg"})}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["If you are shaking your head at this, I have to tell you that I have seen this approach many times. This little helper introduces a problem into the test automation suite. The script closes the cookie message anytime it runs, but what if the cookie message never appears, even when it should? And how do we test a case when we ",s.jsx("em",{className:"text-octo-teal",children:"want"})," the message to appear?"]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`The key to making a good test automation decision is to have decent technical knowledge. Digging a little bit deeper into the cookie message functionality reveals that when we click the "accept all" button, a setting (in the form of a cookie) is saved into the browser storage. This ensures that we don't see the message on the same page over and over again.`}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-cookie-banner-2.png",alt:"Cookie banner visual concept 2",className:"max-w-md w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This is now a functionality we can test! Even better, it's a functionality that we can control. Since most test automation frameworks always open browser with storage cleaned up, we can control when the cookie message appears by choosing when to inject the consent cookie into the browser."}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-3-phases.png",alt:"Conceptual graph showing the concept of 3 phases of test automation",className:"max-w-xl w-full rounded-lg"})}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Making good test automation decisions"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"There's a very popular idea in the world of software development known as DRY - don't repeat yourself. It's a simple principle stating that whenever you have a piece of code that needs to be used at multiple places, it should not be repeated, but rather abstracted. Many testers apply this principle to their test automation code."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Let's again demonstrate this in a simple example."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Imagine that there are 500 end-to-end tests written for an application that requires users to log in. For the sake of simplicity we'll assume that every one of these 500 tests require the user to be logged in."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:'Applying the DRY principle to our test code means that we are going to create a login function that will get called at the beginning of each test as the "arrange" section.'}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["And while the code is being abstracted, executing the login sequence in 500 tests could mean that simply logging in will take 25 minutes of the whole test run (given that login sequence takes 3 seconds). For some tests this can create a bit of imbalance in how long each part of the test takes. After all, the reason why we write tests is more connected to the ",s.jsx("em",{className:"text-octo-teal",children:"act"})," and the"," ",s.jsx("em",{className:"text-octo-teal",children:"assertion"})," phases."]}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-dominant-arrange.png",alt:"3 phase test automation stages with dominant arrange phase",className:"max-w-xl w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"The good news is that this draws a clear picture on what we need to focus on if we want to optimize."}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`In this case, we can use the same principle as we did with our cookies. It's pretty much the same idea as when checking the "remember me" box when logging in to a page. But instead of using it to automatically log in to your favourite social media, you can simulate the same behavior in your tests.`}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"In principle, this approach has the goal of creating relevant contexts and then re-using them. Instead of starting each test from scratch, we want to reuse contexts in as many tests as we can. Login is a great candidate for this, because login does not change from test to test and it's also rarely affected by other test phases."}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-optimization.webp",alt:"Visual representation of test automation optimization, 3 phase concept",className:"max-w-xl w-full rounded-lg"})}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Modern test automation tools such as"," ",s.jsx("a",{href:"https://playwright.dev/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Playwright"})," ","and"," ",s.jsx("a",{href:"https://www.cypress.io/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Cypress"})," ","already have tools for caching login sessions. This approach is not yet widely adopted, but it is the best way of dealing with applications that use login."," ",s.jsx("a",{href:"https://octomind.dev/docs/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Octomind"})," ","provides an option to set up a shared authentication state as well as native support for one-time passwords."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This not only optimizes the test performance, but reduces the amount of login attempts. This helps tremendously when testing applications that have rate-limiting or captcha protection against brute force attacks. These can sometimes be a significant hurdle when it comes to test automation."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Improving test actions"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`Another area of improvement is the "act" section of the test. While it's not the obvious first candidate, there's a lot of potential in making tests faster and therefore making the test automation worth it.`}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:`There's a lot that happens in the "act" phase when doing e2e testing. It's a good practice to ask whether parts of this phase are being reused across tests. Let's say you are testing a to-do app. There's a good chance that you need to create an initial todo item (or more) to meet your testing goal. This can potentially bloat your "act" phase.`}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-todo-creation.png",alt:"3 phases of test automation of to-do app with to do creation included in the act phase",className:"max-w-xl w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Usually when following the DRY principle, testers abstract creation of todo items into its own function. And this is a good thing to do, because now an action can be reused in multiple tests."}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-repeated-necessity.png",alt:"Conceptual graph showing repeated necessity of creating todos steps in testing to-do app",className:"max-w-xl w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"But if you stop and think about it, isn't creating todo items the part of our test that is responsible for arranging the test?"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"It definitely is!"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This means that if we want to draw our graph correctly, it should look more like this:"}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-todo-creation.png",alt:"3 phases of test automation of to-do app with to do creation included in the act phase",className:"max-w-xl w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"It now seems that we have once again made the arrange part of our test too big. But this is once again a situation where we can apply the same principle as with login. Once we understand the parts that create the setup of our tests, we can simply abstract them and reuse and optimize them across different tests."}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Test chaining and dependency structure"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Chaining tests using dependencies is a best practice to structure test automation efficiently. In an automated approach, each test case represents a small task in the user flow you want to achieve. Testing an entire user flow is the execution of a sequence of test cases chained together."}),s.jsxs("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:["Every test case is executed only once which reduces test runtime significantly. It follows the DRY principle allowing for easier test maintenance. Separation minimizes the number of test code adjustments when the code in your app changes. This is valid for both, hand-written tests in"," ",s.jsx("a",{href:"https://playwright.dev/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Playwright"})," ","or"," ",s.jsx("a",{href:"https://www.cypress.io/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Cypress"})," ","and autogenerated tests by"," ",s.jsx("a",{href:"/",rel:"noopener noreferrer",className:"underline",children:"Octomind"}),"."]}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Dependency structure provides you and your collaborators with a better oversight when your test suite gets bigger. It gets harder to understand what each test is doing or how much of your app is actually covered over time."}),s.jsx("figure",{className:"my-8 flex justify-center",children:s.jsx("img",{src:"/assets/blogs/stop-automating-dependency-tree.png",alt:"Dependency tree test arrangement in Octomind testing tool, screenshot",className:"max-w-2xl w-full rounded-lg"})}),s.jsx("h2",{className:"text-2xl md:text-3xl font-bold text-white mb-6 mt-12",children:"Test cases vs. test scripts"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"This brings us to the final difference between manual testing with test cases and test script automation. Usually, when test cases are put together, they follow a pattern of user behavior. These behaviors are then added to groups, so that testing is done within a scope of given features and functionalities. Test scripts, while they should mimic user behavior, do not need to follow the same grouping. The grouping can be done based on the setup each test needs."}),s.jsx("hr",{className:"my-8 border-border"}),s.jsx("p",{className:"text-octo-text text-lg leading-relaxed mb-6",children:"Testing is highly analytical work and requires a good knowledge of the system under test. Test automation requires it all, but also requires some analytical work on what needs to be broken down in order to run tests in an optimal way. Many times, the optimization process is like a mathematics equation, where you can subtract parts that are redundant. A manual approach with test cases is full of these parts and the art of effective test automation decision lies in identifying them."})]}),s.jsx("div",{className:"mt-16",children:s.jsx(Ee,{name:"Veith Röthlingshöfer",title:"senior engineer at Octomind",imageSrc:"/assets/blogs/veith-author.webp"})}),s.jsx(ge,{})]}),s.jsx(se,{})]}),dR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-6 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold mb-4 text-octo-teal",children:"how to start with e2e testing from scratch?"}),s.jsx("p",{className:"animate-fade-in-up text-xl text-muted-foreground mb-8",children:"An Octomind tutorial for developers who want start end-to-end testing"}),s.jsxs("div",{className:"animate-fade-in-up prose prose-invert max-w-none",children:[s.jsx("div",{className:"flex justify-center mb-8",children:s.jsx("img",{src:"/assets/blogs/e2e-tutorial-hero.png",alt:"octomind app screenshot 10/24",className:"rounded-lg max-w-2xl w-full"})}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"When we're under pressure to ship features faster - testing quality is usually the biggest casualty. Similarly, when applications with a lack of unit and integration tests start to suffer from bugs that should've been caught earlier, end-to-end tests are often brought in to catch regressions as they can cover the most critical user flows faster than ramping up TDD efforts."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"But without a dedicated testing team, learning, configuring, and integrating automation frameworks such as Playwright also takes developers away from writing code and shipping features."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"That's why we're building Octomind. We use AI to automate the creation of an end-to-end test suite from scratch, and without any prior automation testing experience."}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Let me take you through what getting up to speed with Octomind looks like so you can see if it's something you'd want to try out for your own websites or apps."}),s.jsxs("ol",{className:"list-decimal list-inside text-lg text-muted-foreground mb-8 space-y-2",children:[s.jsx("li",{children:s.jsx("a",{href:"#getting-started-1",className:"underline",children:"Getting started"})}),s.jsx("li",{children:s.jsx("a",{href:"#getting-started-2",className:"underline",children:"Test creation"})}),s.jsx("li",{children:s.jsx("a",{href:"#getting-started-3",className:"underline",children:"Test running"})}),s.jsx("li",{children:s.jsx("a",{href:"#getting-started-4",className:"underline",children:"Test coverage"})}),s.jsx("li",{children:s.jsx("a",{href:"#getting-started-5",className:"underline",children:"Test status & debugging"})})]}),s.jsx("h2",{id:"getting-started-1",className:"text-2xl font-bold mt-12 mb-6",children:"1. Getting started"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["We make it as easy as possible to get started. Just enter a"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"publicly accessible URL"})," for the site you want to test and you're away."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-signin.webp",alt:"octomind app sign-in, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"octomind app sign-in, 10/24"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"But Octomind isn't limited to public URLs. Only the onboarding flow needs that to create a 'known good' state or 'baseline' in testing terminology for future deployments to be tested and verified against. The entire test suite can be run in any internal environment (including development) with a single API call or SDK call."}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-test-report.webp",alt:"octomind app test report, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"octomind app test report, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["But this is jumping ahead a bit and I'll cover this in the"," ",s.jsx("a",{href:"#getting-started-3",className:"underline italic text-octo-teal",children:"Test running"})," ","section."]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Getting back to the sign-up flow, we suggest using the URL as the project name, but totally up to you. Then we've got the standard create account and log in flow, and once logged in Octomind's AI agent (I'll call it ",s.jsx("strong",{className:"text-octo-purple-light",children:"Agent"})," from here on in) checks it can access your site, then gets to work creating test cases."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-signup.webp",alt:"octomind app sign-up, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"octomind app sign-up, 10/24"})]}),s.jsx("h2",{id:"getting-started-2",className:"text-2xl font-bold mt-12 mb-6",children:"2. Test creation"}),s.jsx("h4",{className:"text-lg font-semibold mb-4",children:"So how does Octomind know what to test?"}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"Our LLMs are continuously trained and evaluated against thousands of different types of sites and applications. With this data set, it can classify each site type and create a unique context for deriving relevant test cases."}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-agent-generating.webp",alt:"AI Agent generating tests for an e-commerce site 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"Agent generating tests for an e-commerce site, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["The Agent always starts with ",s.jsx("em",{className:"text-octo-teal",children:"detecting cookie banners"})," and"," ",s.jsx("em",{className:"text-octo-teal",children:"required login forms"})," because unless you're testing the login flow itself, most tests will require one or both of these to complete before every test run."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-cookie-banner.webp",alt:"AI auto-generated cookie banner test, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"auto-generated cookie banner test, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["If it finds a required login form, you'll be prompted to enter"," ",s.jsx("em",{className:"text-octo-teal",children:"testing credentials"})," - or just leave them blank to test unauthenticated."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-credentials.webp",alt:"credentials input for a login test, 10/24",className:"rounded-lg max-w-md w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"credentials input for a login test, 10/24"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"You can also enter them later from the settings panel for use in a future login test."}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-account-settings.webp",alt:"test account in octomind app account settings, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test account in octomind app account settings, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Octomind then analyzes your site to determine the most logical initial set of test cases, with the"," ",s.jsx("em",{className:"text-octo-teal",children:"test stack view"})," showing its progress and the steps for each test case."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-test-stack.webp",alt:"test stack view showing Agent progress when generating, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test stack view showing Agent progress when generating, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["The Agent typically starts with three to four test cases. This isn't a system limitation, but a practical one - as the best way to"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"scale end-to-end test coverage is to start small, then expand incrementally."})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"The initial test implementation can take a bit of time (we're working on it!). As the Agent completes each test case, the number of total and active tests is updated - active meaning the test executed and passed successfully."}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-project-overview.webp",alt:"project overview page with active test cases, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"project overview page with active test cases, 10/24"})]}),s.jsx("h2",{id:"getting-started-3",className:"text-2xl font-bold mt-12 mb-6",children:"3. Test running"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Once all test cases are implemented, Octomind runs the active tests - creating the"," ",s.jsx("em",{className:"text-octo-teal",children:"first test report"}),"."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-first-results.webp",alt:"first test results after sign-up, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"first test results after sign-up, 10/24"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"To go deeper, the test report details page provides a step-by-step visual representation for each test case."}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-snapshots.webp",alt:"test run snapshots in test report detail, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test run snapshots in test report detail, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Or you can debug a specific test case by inspecting the"," ",s.jsx("a",{href:"https://playwright.dev/docs/trace-viewer",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Playwright Trace Viewer"})," ","or run the agent-generated code locally for step-debugging."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-debugging-tab.webp",alt:"test run detail, debugging tab, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test run detail, debugging tab, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Test runs can be ",s.jsx("em",{className:"text-octo-teal",children:"scheduled"}),"."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-scheduler.webp",alt:"test run scheduler, 10/24",className:"rounded-lg max-w-md w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test run scheduler, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["And triggered on-demand in"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"a post-deployment task in CI/CD via our integrations or API"}),"."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-api-key.webp",alt:"API key for CI integrations in Octomind app settings, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"API key for CI integrations in Octomind app settings, 10/24"})]}),s.jsx("h2",{id:"getting-started-4",className:"text-2xl font-bold mt-12 mb-6",children:"4. Test coverage"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["This is where things get interesting. You can increase your test coverage"," ",s.jsxs("strong",{className:"text-octo-purple-light",children:["simply by asking the Agent to ",s.jsx("em",{className:"text-octo-teal",children:"generate more"})," tests."]})]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/6706abc4c6ea64060bb750b6_Screenshot 2024-10-07 at 16.57.49.webp",alt:"telling the AI agent to generate sequential tests, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"telling the Agent to generate sequential tests, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["You can also get suggestions for new tests or you create a"," ",s.jsxs("strong",{className:"text-octo-purple-light",children:["custom test case by ",s.jsx("em",{className:"text-octo-teal",children:"prompting"})," the Agent"]}),"."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-custom-test.webp",alt:"prompting the AI agent to generate a custom test",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"prompting the Agent to generate a custom test, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Test cases arranged in a"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"hierarchy, visually showing the execution sequence from parent to child"})," ","while also providing the Agent with context for the new test cases."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-dependency-view.webp",alt:"dependency view showing the execution sequence of test cases, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"dependency view showing the execution sequence of test cases, 10/24"})]}),s.jsx("h2",{id:"getting-started-5",className:"text-2xl font-bold mt-12 mb-6",children:"5. Test status and debugging"}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:[s.jsx("em",{className:"text-octo-teal",children:"Active"})," indicates the test step design worked as expected, whereas an"," ",s.jsx("em",{className:"text-octo-teal",children:"inactive test (off)"})," means the Agent hit a stumbling block that prevented it from reaching the test verification step."," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Nothing is wrong with your site"})," - the Agent just needs help refining its test strategy. It's labeled as ",s.jsx("em",{className:"text-octo-teal",children:"'steps need review'"}),"."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-inactive-test.webp",alt:"inactive generated test where steps need review, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"inactive generated test where steps need review, 10/24"})]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-6",children:"There are numerous options for fixing an inactive test."}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["The agent-generated ",s.jsx("em",{className:"text-octo-teal",children:"prompt"})," can be altered to include steps it didn't think of."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-debug-prompt.webp",alt:"debugging an agent-generated prompt of a test case, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"debugging an agent-generated prompt of a test case, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["The ",s.jsx("em",{className:"text-octo-teal",children:"locator"})," (element to interact with) can be adjusted (e.g. if the wrong button was clicked)."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-locator-picker.webp",alt:"hover in snapshot to activate the visual locator picker to change a locator, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"hover in snapshot to activate the visual locator picker to change a locator, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["The ",s.jsx("strong",{className:"text-octo-purple-light",children:"assertion and interaction methods"})," can be changed (e.g. if it's not verifying the state of the correct element)."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-assertion-method.webp",alt:"changing interaction & assertion method with visual locator picker, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"changing interaction & assertion method with visual locator picker, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["You can ",s.jsx("em",{className:"text-octo-teal",children:"delete"})," redundant steps."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-step-deletion.webp",alt:"test step deletion, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"test step deletion, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Plus you can ",s.jsx("em",{className:"text-octo-teal",children:"run and debug"})," the test locally."]}),s.jsxs("figure",{className:"my-8 flex flex-col items-center",children:[s.jsx("img",{src:"/assets/blogs/e2e-tutorial-local-debug.webp",alt:"local test running & debugging, 10/24",className:"rounded-lg max-w-lg w-full"}),s.jsx("figcaption",{className:"text-sm text-muted-foreground mt-2",children:"local test running & debugging, 10/24"})]}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-6",children:["Once the test steps have been fixed, click ",s.jsx("em",{className:"text-octo-teal",children:"'save and run'"})," to execute the test and if successful, it will change to an active test."]}),s.jsx("p",{className:"text-lg text-muted-foreground mb-8",children:"That's a pretty comprehensive start-to-finish tour of Octomind. Hopefully you're ready to give it a try."}),s.jsxs("p",{className:"text-lg text-muted-foreground mb-8",children:["And if you have any questions, you can email me at daniel @ octomind.dev or join our"," ",s.jsx("a",{href:"https://discord.gg/3ShnZMKRfA",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Discord community"}),"."]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder & CPO",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),uR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsxs("div",{className:"mb-8 animate-fade-in-up",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"our problem with backlogs"}),s.jsx("p",{className:"text-xl text-muted-foreground",children:"A backlog is a collection of tasks that aren't important enough to do. If they were, we wouldn't be stashing them away."})]}),s.jsx("div",{className:"mb-12 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/66b63ba66735e61b1b241360_backlog-octo.webp",alt:"Backlog octopus illustration",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none animate-fade-in-up",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Over a year ago, we launched our dev tooling startup. One of the biggest pitfalls for new companies is focusing on the wrong priorities - over-engineering your scaling capabilities too early or neglecting customer value."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"From the outset, we decided to create a culture where everyone truly owns their work. This approach not only ensures we're focusing on the right things. As an engineer, I have always enjoyed working most on the things I find important."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:'One of our early discussions was about adopting a "no backlog" approach. Now, with a team of 8 engineers and over a year of experience, we revisited this decision to see if it still made sense.'}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It was a fun exercise. We got our backlog frustrations from previous workplaces out of the system, and discussed the advantages and disadvantages we have identified in the past year. I've summarized it here."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"If you consider deleting your whole backlog or moving to a less backlog-centric approach of software development, I hope to give you some good insights."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-6",children:"What exactly is a backlog?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"To start the discussion in the now larger team, I wanted to get a feeling for the backlog vibe. When I asked around, I got answers like:"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"An ordered list of tasks that we need to complete"}),s.jsx("li",{children:"A way to structure work"}),s.jsx("li",{children:"A way to remember past discussions and conversations"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"But also:"}),s.jsxs("ul",{className:"text-octo-text list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"A dumping ground for unimportant tasks"}),s.jsx("li",{children:"A place I can put stuff in. I know we'll never work on that. It gives me a good feeling of putting it there."}),s.jsx("li",{children:"A to-do list that never gets done"}),s.jsx("li",{children:"A good tool to shut down discussions"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["For me personally, a backlog is a collection of tasks that aren't important enough to do"," ",s.jsx("strong",{children:s.jsx("em",{className:"text-octo-teal",children:"right now"})}),". If they were, we wouldn't be stashing them away. After years in software development I've come to see backlogs as more of a hindrance than a help."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["When I refer to 'backlog' I do ",s.jsx("strong",{className:"text-octo-purple-light",children:"NOT"})," mean a list of tasks that you need to do in the next short iteration (1-2 weeks), but anything longer than that (as in 'product backlog')."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"So, let's dive into some of the problems I see with backlogs."}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"1. They are designed to be a dishonest conversation"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Backlogs often lead to some pretty dishonest conversations. Picture this: A stakeholder"," ",s.jsx("strong",{children:s.jsx("em",{className:"text-octo-teal",children:"urgently"})})," ","requests a new feature, as is often the case. The product owner, instead of outright saying, 'This isn't a priority right now,' adds it to the backlog. The stakeholder walks away thinking their request will be addressed in x days, while the product owner knows it probably won't be touched."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It's an easier conversation to have than just saying we will never get to it because it's not important. This is what's happening every time you add something to a really long product backlog. This cycle keeps everyone happy on the surface but doesn't solve the real issue."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-meme.png",alt:"backlog meme",className:"max-w-md rounded-lg"})}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"2. The never ending list"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Backlogs have a large gap between expectation and reality even for the most experienced of teams. We all would like to see a nice, steady burn-down of tasks. In reality, backlogs rarely shrink. If it's still growing after the 12th iteration, that just breeds frustration for everyone involved."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"An empty backlog is a myth. We've never seen such a thing in any of the companies that anyone in our team has ever worked in. Unless we decided to delete the old and start with a new one out of sheer frustration."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-expectation-reality.png",alt:"expectation vs reality graph when talking about backlogs",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["graphs source:"," ",s.jsx("a",{href:"https://www.allankelly.net/static/presentations/Shrunk.pdf",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"https://www.allankelly.net/static/presentations/Shrunk.pdf"})]}),s.jsxs("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:["3. They are a ",s.jsx("em",{className:"text-octo-teal",children:"COLOSSAL"})," waste of time"]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Maintaining a backlog eats up a lot of time. Teams spend countless hours refining, grooming, and updating tasks, creating wireframes, and ensuring every detail is documented. Yet, by the time a task actually is being worked on, the original requirements or context may have changed substantially, making all that prep work useless."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Every moment spent on a task that isn't immediately being picked up is time wasted. The gap between defining and implementing tasks often means the work becomes obsolete as requirements and reality change, as they always do in software."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-waste-of-time.png",alt:"backlog a waste of time",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["source:"," ",s.jsx("a",{href:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html"})]}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"4. They shift bottlenecks (and blame) between different roles"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Imagine a ",s.jsx("strong",{className:"text-octo-purple-light",children:"customer success manager"})," who asks for a specific feature for a specific customer. The dev team has an open backlog policy, everyone is allowed to add a ticket. The engineering team has to take a look at the item, refine it, then potentially estimate the effort (#noEstimates, but we are trying to focus here). The refined task never makes it to an iteration. Different prios."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["The blame for not completing the task on time lies with the engineering team. If they"," ",s.jsx("strong",{children:s.jsx("em",{className:"text-octo-teal",children:"ONLY"})})," ","had worked faster."]}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-bottleneck-no-po.png",alt:"backlog bottlenecks without PO",className:"max-w-xl rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["source:"," ",s.jsx("a",{href:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:["So you add a ",s.jsx("strong",{className:"text-octo-purple-light",children:"product owner"})," to the front of the process. We don't believe the role must be tied to a specific person, but the concept is solid. The prioritization is now done with the team at a much higher level of abstraction BEFORE stuff ends up in the backlog."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Ideally, you've matched the input rate and the output rate. Now, It's much faster and cheaper to throw stuff away."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-bottleneck-with-po.png",alt:"backlog bottleneck with a PO",className:"max-w-xl rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["source:"," ",s.jsx("a",{href:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"https://lucasfcosta.com/2023/02/07/backlogs-are-useless.html"})]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:["Part of our problem with ",s.jsx("strong",{className:"text-octo-purple-light",children:"dedicated product owners"})," is that they take away too much ownership from the team. They often just take blame from both stakeholders AND dev teams, and can quickly become a single point of failure for the product's success."]}),s.jsxs("ul",{children:[s.jsx("li",{children:"Are they good at prioritizing and keeping stakeholders at bay?"}),s.jsx("li",{children:"Do they have enough authority to make decisions?"}),s.jsx("li",{children:"Are they blocking development by being too protective of the backlog?"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Something that came up in the discussion summarized it perfectly:"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8",children:s.jsx("span",{className:"text-octo-teal",children:`"We used to have some fierce guardians of the backlog at my previous company. It was impossible to get anything in. Didn't go well for either the product or the company."`})}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"5. Backlog bias when discussing importance"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Backlogs also introduce significant biases. Tasks added to the backlog represent decisions made without the necessary input from the current context. As time goes on and things change, these tasks might no longer be the best solution. Yet, teams continue with them simply because they're in the backlog."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"I've seen this happen over and over: a task sits in the backlog for months, gets discussed in every iteration as critical, only to become irrelevant when it's finally addressed."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-6",children:"So, what did we do instead?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This quote of Allen Holub is about more than backlog size. It gives us a way forward without one."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/backlog-allen-holub-tweet.png",alt:"allen holub backlog twitter",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["source:"," ",s.jsx("a",{href:"https://x.com/allenholub",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"https://x.com/allenholub"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:"In product development, detailed long-term planning should be limited to high-level priorities — knowing what to build and why. The next steps become clear through building and immediate feedback. A backlog often gets too granular for planning beyond the next 1-2 weeks."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["When you ask developers about the most important task, they're usually already aligned. While there may be minor differences in preferences, identifying the top priority"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"right now"})," isn't typically an issue."]}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"1. Continuous Discovery"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We adopted 'Continuous Discovery' practices, focusing on ongoing conversations with customers and stakeholders to gather insights and feedback. It's about always reassessing priorities based on the latest information, ensuring that we are working on what matters most."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Rather than relying on a static backlog, our process is fluid, with priorities shifting as new information emerges."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["However, we're not starting from scratch each week; the Opportunity system within Continuous Discovery helps us stay on course. Opportunities highlight customer needs and pain points"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"without jumping prematurely into solutions"})," on a low abstraction level until absolutely necessary. They ensure we tackle the most critical issues first."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["For a deeper dive, check out Teresa Torres' explanation in"," ",s.jsx("a",{href:"https://www.producttalk.org/2023/12/opportunity-solution-trees/#whats-an-opportunity",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Product Talk"}),"."]}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"2. Aligning with goals"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["A key strategy is to focus on"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"overarching business goals"})," instead of specific tasks. This ensures our efforts align with the bigger picture and adapt to current priorities. This goal-oriented approach helps us identify critical problems without getting stuck on a fixed list."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This is, of course, the bread and butter of engineering and product leadership in all companies. It's also really hard. We often struggle with making our goals measurable for small increments for example."}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"3. The case for not listing ideas"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8",children:s.jsx("span",{className:"text-octo-teal",children:`"But what about all my good ideas that I will lose if I don't write them into a backlog?"`})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["First, I really think that if the idea is good enough"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"it will"})," come up again, because you will spend more time thinking about the problem space. You might come up with an even better solution."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"You should always rethink your ideas. You should be able to kill your darlings, if they don't serve the purpose anymore."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"More than a year into Octomind, we know what ideas were discussed and discarded, and why. We have the advantage of being a small team, so onboarding new colleagues to our mental model is still easy. I don't think a backlog of outdated items helps the newbies anyways."}),s.jsx("h3",{className:"text-white text-xl font-semibold mt-8 mb-4",children:"4. Cheating with mini-backlogs"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["We collect ",s.jsx("strong",{className:"text-octo-purple-light",children:"bug tickets"}),", sort critical ones in real-time and run a bug triage every week to address the rest. We have a zero-bug-policy, which means they are ",s.jsx("strong",{className:"text-octo-purple-light",children:"just as important"})," as any of the tasks that we planned for that iteration, and this ensures the bug backlog doesn't grow indefinitely. Most of us get anxious if there's more than one page of open GitHub issues. It seems to be working for us."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Is it cheating? You decide."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-6",children:"This might not work for you"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Our business model relies on other vendors' LLMs, a landscape that shifts constantly. We must adapt quickly to avoid investing in solutions that could be outdated with the next GPT release."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"I'm very well aware that not all software development is the same. Some ecosystems require long-term planning, extended development cycles, and careful rollout, especially in industries with strict regulations or safety-critical applications (looking at you CrowdStrike). Large engineering teams need robust processes so the house doesn't fall apart."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We might eventually outgrow this set-up and introduce more backlog-like processes. Until then, we'll do our best to keep the backlog monster locked away."})]}),s.jsxs("div",{className:"mt-16 animate-fade-in-up",children:[s.jsx(Ee,{name:"Daniel Draper",title:"Lead Octoneer",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),hR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 text-octo-teal animate-fade-in-up",children:"why we no longer use LangChain for building our AI agents"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8 animate-fade-in-up",children:"When abstractions do more harm than good: Lessons learned using LangChain in production and what we should've done instead"}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/666abdd1a2dd970e1452b269_DYI-octos.webp",alt:"DIY Octopus illustration",className:"w-full rounded-lg mb-12"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["At Octomind, we use AI agents with multiple LLMs to automatically create and fix end-to-end tests in Playwright. Until just a few months ago, we did it with the"," ",s.jsx("a",{href:"https://www.langchain.com/",className:"underline",children:"LangChain framework"}),"."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"In this post I'll share our struggles with LangChain and why replacing its rigid high-level abstractions with modular building blocks simplified our code base and made our team happier and more productive."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The backstory"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We used LangChain in production for over 12 months, starting in early 2023 then removing it in 2024."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:['LangChain seemed to be the best choice for us in 2023. It had an impressive list of components and tools, and its popularity soared. It promised to "',s.jsx("em",{className:"text-octo-teal",children:"enable developers to go from an idea to working code in an afternoon."}),'" But problems started to surface as our requirements became more sophisticated, turning LangChain into a source of friction, not productivity.']}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"As its inflexibility began to show, we soon found ourselves diving into LangChain internals, to improve lower-level behavior of our system. But because LangChain intentionally abstracts so many details from you, it often wasn't easy or possible to write the lower-level code we needed to."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The perils of being an early framework"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"AI and LLMs are rapidly changing fields, with new concepts and ideas popping up weekly. So when a framework such as LangChain is created around multiple emerging technologies, designing abstractions that will stand the test of time is incredibly difficult."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"I'm sure that If I had attempted to build a framework such as LangChain when they did, I wouldn't have done any better. Mistakes are easy to point out in hindsight, and the intent of this post is not to unfairly criticize LangChain's core developers or their contributors. Everyone is doing the best they can."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Crafting well-designed abstractions is hard"})," - even if the requirements are well-understood. But when you're modelling components in such a state of flux (e.g. agents), it's a safer bet to use abstractions for lower level building blocks only."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The problem with LangChain's abstractions"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"LangChain was helpful at first when our simple requirements aligned with its usage presumptions. But its high-level abstractions soon made our code more difficult to understand and frustrating to maintain. When our team began spending as much time understanding and debugging LangChain as it did building features, it wasn't a good sign."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Issues with LangChain's approach to abstractions can be demonstrated with this trivial example of translating an English word into Italian."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Here is a Python example using just the OpenAI package:"}),s.jsx("div",{className:"my-8 rounded-lg overflow-hidden border border-white/10",children:s.jsx("div",{className:"bg-[#0d0d1a] p-4 overflow-x-auto",children:s.jsx("pre",{style:{fontSize:"13px",lineHeight:"1.6",fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:" 1 "}),s.jsx("span",{style:{color:"#c792ea"},children:"from"})," ",s.jsx("span",{style:{color:"#82aaff"},children:"openai"})," ",s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"OpenAI"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 2 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 3 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"client"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"OpenAI"}),s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#C3C9D3"},children:"api_key"}),s.jsx("span",{style:{color:"#89ddff"},children:"="}),s.jsx("span",{style:{color:"#c3e88d"},children:'""'}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 4 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"text"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"hello!"'}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 5 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"language"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"Italian"'}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 6 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 7 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"messages"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#89ddff"},children:"["}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 8 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"{"}),s.jsx("span",{style:{color:"#c3e88d"},children:'"role"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"system"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"content"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"You are an expert translator"'}),s.jsx("span",{style:{color:"#89ddff"},children:"}"}),s.jsx("span",{style:{color:"#89ddff"},children:","}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 9 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"{"}),s.jsx("span",{style:{color:"#c3e88d"},children:'"role"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"user"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"content"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsxs("span",{style:{color:"#c3e88d"},children:['f"Translate the following from English into ',"{"]}),s.jsx("span",{style:{color:"#C3C9D3"},children:"language"}),s.jsxs("span",{style:{color:"#c3e88d"},children:["}",'"']}),s.jsx("span",{style:{color:"#89ddff"},children:"}"}),s.jsx("span",{style:{color:"#89ddff"},children:","}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"10 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"{"}),s.jsx("span",{style:{color:"#c3e88d"},children:'"role"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"user"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"content"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsxs("span",{style:{color:"#c3e88d"},children:['f"',"{"]}),s.jsx("span",{style:{color:"#C3C9D3"},children:"text"}),s.jsxs("span",{style:{color:"#c3e88d"},children:["}",'"']}),s.jsx("span",{style:{color:"#89ddff"},children:"}"}),s.jsx("span",{style:{color:"#89ddff"},children:","}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"11 "}),s.jsx("span",{style:{color:"#89ddff"},children:"]"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"12 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"13 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"response"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"client"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"chat"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"completions"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#82aaff"},children:"create"}),s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#C3C9D3"},children:"model"}),s.jsx("span",{style:{color:"#89ddff"},children:"="}),s.jsx("span",{style:{color:"#c3e88d"},children:'"gpt-4o"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"messages"}),s.jsx("span",{style:{color:"#89ddff"},children:"="}),s.jsx("span",{style:{color:"#C3C9D3"},children:"messages"}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"14 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"result"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"response"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"choices"}),s.jsx("span",{style:{color:"#89ddff"},children:"["}),s.jsx("span",{style:{color:"#f78c6c"},children:"0"}),s.jsx("span",{style:{color:"#89ddff"},children:"]"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"message"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"content"})]})})})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This is simple code that is easy to understand, containing a single class and one function call. The rest is standard Python."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Let's contrast this with LangChain's version:"}),s.jsx("div",{className:"my-8 rounded-lg overflow-hidden border border-white/10",children:s.jsx("div",{className:"bg-[#0d0d1a] p-4 overflow-x-auto",children:s.jsx("pre",{style:{fontSize:"13px",lineHeight:"1.6",fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:" 1 "}),s.jsx("span",{style:{color:"#c792ea"},children:"from"})," ",s.jsx("span",{style:{color:"#82aaff"},children:"langchain_openai"})," ",s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"ChatOpenAI"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 2 "}),s.jsx("span",{style:{color:"#c792ea"},children:"from"})," ",s.jsx("span",{style:{color:"#82aaff"},children:"langchain_core.output_parsers"})," ",s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"StrOutputParser"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 3 "}),s.jsx("span",{style:{color:"#c792ea"},children:"from"})," ",s.jsx("span",{style:{color:"#82aaff"},children:"langchain_core.prompts"})," ",s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"ChatPromptTemplate"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 4 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 5 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"os"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#C3C9D3"},children:"environ"}),s.jsx("span",{style:{color:"#89ddff"},children:"["}),s.jsx("span",{style:{color:"#c3e88d"},children:'"OPENAI_API_KEY"'}),s.jsx("span",{style:{color:"#89ddff"},children:"]"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'""'}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 6 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"text"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"hello!"'}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 7 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"language"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"Italian"'}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 8 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 9 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"prompt_template"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"ChatPromptTemplate"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#82aaff"},children:"from_messages"}),s.jsx("span",{style:{color:"#89ddff"},children:"("}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"10 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"["}),s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"system"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"You are an expert translator"'}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),s.jsx("span",{style:{color:"#89ddff"},children:","}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"11 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"user"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsxs("span",{style:{color:"#c3e88d"},children:['"Translate the following from English into ',"{"]}),s.jsx("span",{style:{color:"#C3C9D3"},children:"language"}),s.jsxs("span",{style:{color:"#c3e88d"},children:["}",'"']}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),s.jsx("span",{style:{color:"#89ddff"},children:","}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"12 "})," ",s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"user"'}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsxs("span",{style:{color:"#c3e88d"},children:['"',"{"]}),s.jsx("span",{style:{color:"#C3C9D3"},children:"text"}),s.jsxs("span",{style:{color:"#c3e88d"},children:["}",'"']}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),s.jsx("span",{style:{color:"#89ddff"},children:"]"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"13 "}),s.jsx("span",{style:{color:"#89ddff"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"14 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"15 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"parser"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"StrOutputParser"}),s.jsx("span",{style:{color:"#89ddff"},children:"()"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"16 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"chain"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"prompt_template"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"|"})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"model"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"|"})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"parser"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:"17 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"result"})," ",s.jsx("span",{style:{color:"#89ddff"},children:"="})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"chain"}),s.jsx("span",{style:{color:"#89ddff"},children:"."}),s.jsx("span",{style:{color:"#82aaff"},children:"invoke"}),s.jsx("span",{style:{color:"#89ddff"},children:"("}),s.jsx("span",{style:{color:"#89ddff"},children:"{"}),s.jsx("span",{style:{color:"#c3e88d"},children:'"language"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"language"}),s.jsx("span",{style:{color:"#89ddff"},children:","})," ",s.jsx("span",{style:{color:"#c3e88d"},children:'"text"'}),s.jsx("span",{style:{color:"#89ddff"},children:":"})," ",s.jsx("span",{style:{color:"#C3C9D3"},children:"text"}),s.jsx("span",{style:{color:"#89ddff"},children:"}"}),s.jsx("span",{style:{color:"#89ddff"},children:")"})]})})})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:"The code is roughly doing the same, but that's where the similarities end."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We now have three classes and four function calls. But most concerning, is the introduction of three new abstractions:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-2",children:[s.jsx("li",{children:"Prompt templates: Providing a prompt to the LLM"}),s.jsx("li",{children:"Output parsers: Processing output from the LLM"}),s.jsxs("li",{children:[`Chains: LangChain's "LCEL syntax" overriding Python's`," ",s.jsx("code",{className:"bg-[#0d0d1a] px-1.5 py-0.5 rounded text-octo-teal text-sm",children:"|"})," operator"]})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:"All LangChain has achieved is increased the complexity of the code with no perceivable benefits."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["This code might be fine for early-stage prototypes. But"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"for production usage, every component must be reasonably understood"})," ","so it won't blow up on you unexpectedly under real-world usage conditions. You have to adhere to the given data structures and design your application around those abstractions."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Let's look at another abstraction comparison in Python, this time for fetching JSON from an API."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Using the built-in http package:"}),s.jsx("div",{className:"my-8 rounded-lg overflow-hidden border border-white/10",children:s.jsx("div",{className:"bg-[#0d0d1a] p-4 overflow-x-auto",children:s.jsx("pre",{style:{fontSize:"13px",lineHeight:"1.6",fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:" 1 "}),s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"http.client"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 2 "}),s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"json"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 3 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 4 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"conn = http.client.HTTPSConnection("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"api.example.com"'}),s.jsx("span",{style:{color:"#C3C9D3"},children:"]"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 5 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"conn.request("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"GET"'}),s.jsx("span",{style:{color:"#C3C9D3"},children:","}),s.jsx("span",{style:{color:"#c3e88d"},children:'"/data"'}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 6 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"response = conn.getresponse()"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 7 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"data = json.loads(response.read().decode())"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 8 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"conn.close()"})]})})})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:"Using the requests package:"}),s.jsx("div",{className:"my-8 rounded-lg overflow-hidden border border-white/10",children:s.jsx("div",{className:"bg-[#0d0d1a] p-4 overflow-x-auto",children:s.jsx("pre",{style:{fontSize:"13px",lineHeight:"1.6",fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:" 1 "}),s.jsx("span",{style:{color:"#c792ea"},children:"import"})," ",s.jsx("span",{style:{color:"#ffcb6b"},children:"requests"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 2 "}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 3 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"response = requests.get("}),s.jsx("span",{style:{color:"#c3e88d"},children:'"https://api.example.com/data"'}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 4 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"data = response.json()"})]})})})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-4",children:"The winner is obvious. That's what a good abstraction feels like."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Granted, these are trivial examples. But my point is that good abstractions simplify your code and reduce the cognitive load required to understand it."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["LangChain tries to make your life easier by doing more with less code by"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"hiding details away from you"}),". But when this comes at the cost of simplicity and flexibility, abstractions lose their value."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["LangChain also has a habit of using"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"abstractions on top of other abstractions"}),", so you're often forced to think in terms of nested abstractions to understand how to use an API correctly. This inevitably leads to comprehending huge stack traces and debugging internal framework code you didn't write instead of implementing new features."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"LangChain's impact on our development team"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Our application makes heavy use of AI agents for performing different types of tasks, such as test case discovery, Playwright test generation, and auto-fixing."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"When we wanted to move from an architecture with a single sequential agent to something more complex, LangChain was the limiting factor. For example, spawning sub-agents and letting them interact with the original agent. Or multiple specialist agents interacting with each other."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"In another instance, we needed to dynamically change the availability of tools our agents could access, based on business logic and output from the LLM. But LangChain does not provide a method for externally observing an agent's state, resulting in us reducing the scope of our implementation to fit into the limited functionality available to LangChain Agents."}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8 text-xl",children:s.jsx("span",{className:"text-octo-teal",children:"Once we removed it, we no longer had to translate our requirements into LangChain appropriate solutions. We could just code."})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"So, if not LangChain, what framework should you be using? Perhaps you don't need a framework at all."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Do you need a framework for building AI applications?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"LangChain helped us early on by providing LLM features so we could focus on building our application. But in hindsight, we would've been better off long-term without a framework."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"LangChain's long list of components gives the impression that building an LLM-powered application is complicated. But the core components most applications will need are typically:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-2",children:[s.jsx("li",{children:"A client for LLM communication"}),s.jsx("li",{children:"Functions/Tools for function calling"}),s.jsx("li",{children:"A vector database for RAG"}),s.jsx("li",{children:"An Observability platform for tracing, evaluation etc."})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The rest are either helpers around those components (e.g. chunking and embeddings for vector databases), or regular application tasks such as managing files and application state through data persistence and caching."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"If you start your AI development journey without a framework, yes, it will take longer to put your own toolbox together and require more upfront learning and research. But this is time well spent and a worthy investment in you and your application's future, since you are learning fundamentals in the field you are going to operate."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["In most cases,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"your usage of LLMs will be simple and straightforward"}),". You'll mostly be writing sequential code, iterating on prompts, and improving the quality and predictability of your output. The majority of tasks can be achieved with simple code and a relatively small collection of external packages."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Even if using agents, it's unlikely you'll be doing much beyond simple agent to agent communication in a predetermined sequential flow with business logic for handling agent state and their responses. You don't need a framework to implement this."}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8 text-xl",children:s.jsx("span",{className:"text-octo-teal",children:"While the Agents space is rapidly evolving with exciting possibilities and interesting use cases, we recommend keeping things simple for now while agent usage patterns solidify."})}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Staying fast and lean with building blocks"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Presuming you're not shipping rubbish code to production, the speed at which a team can innovate and iterate is the most important metric for success. A lot of development in the AI space is driven by experimentation and prototyping."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["But frameworks are typically designed for"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"enforcing structure based on well-established patterns of usage"})," ","- something LLM-powered applications don't yet have. Having to translate new ideas into framework-specific code, limits your speed of iteration."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"A building blocks approach prefers simple low-level code with carefully selected external packages, keeping your architecture lean so developers can devote their attention to the problem they're trying to solve."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["A building block being something simple you feel is comprehensively understood and unlikely to change. For example, a vector database. It's a known type of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"modular component with a baseline set of features so it can easily be swapped"})," ","out and replaced. Your codebase needs to be lean and adaptable to maximize your learning speed and the value you get from each iteration cycle."]}),s.jsx("p",{className:"text-center my-8",children:". . ."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"I hope I thoughtfully and fairly described our challenges with LangChain and why moving away from frameworks altogether has been hugely beneficial for our team."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Our current strategy of using modular building blocks with minimal abstractions allows us to now develop more quickly and with less friction."})]}),s.jsx("div",{className:"mt-16",children:s.jsx(Ee,{name:"Fabian Both",title:"Staff Deep Learning Engineer at Octomind",imageSrc:"/assets/blogs/fabian-both-author.webp"})}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]});function mR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-3xl",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-12",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-4 lowercase",children:"did you break your code or is the test flaky?"}),s.jsx("p",{className:"text-xl text-muted-foreground",children:"A fail-safe checklist for how you can tell the difference"})]}),s.jsx("div",{className:"mb-12 rounded-xl overflow-hidden",children:s.jsx("img",{src:"/assets/blogs/664cae51cf470851c5905e8b_snowflake-octo.webp",alt:"Octopus fixing a computer in a snowy scene",className:"w-full"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Flaky end-to-end tests are frustrating for quality assurance (QA) and development teams, causing constant disruptions and eroding trust in test outcomes due to their unreliability."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"We'll go over all you need to know about flaky tests, how to spot a flaky test from a real problem, and how to handle, fix, and stop flaky tests from happening again."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-6",children:"Are flaky tests a real issue?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"While often ignored, flaky tests are problematic for QA and Development teams for several reasons:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Lack of trust in test results"}),s.jsx("br",{}),"When tests are unreliable, developers and QA teams may doubt the validity of the results, not only for those specific tests but also for the entire set."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Wasted time and resources"}),s.jsx("br",{}),"The time and resources wasted diagnosing flaky tests could've been spent adding value to the business."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Obstructed CI/CD pipelines"}),s.jsx("br",{}),"Constant test failures that are unreliable often result in the need to run tests again to ensure success, causing avoidable delays for downstream CI/CD tasks like producing artifacts and initiating deployments."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Masks real issues"}),s.jsx("br",{}),"Repeated flaky failures may lead QA and Developers to ignore test failures, increasing the risk that genuine defects sneak through and are deployed to production."]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-6",children:"What causes flaky tests?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Flaky tests are usually the result of code that does not take enough care to determine whether the application is ready for the next action."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Take this flaky test Playwright test written in Python:"}),s.jsx("div",{className:"my-8 rounded-lg overflow-hidden border border-white/10",children:s.jsx("div",{className:"bg-[#0d0d1a] p-4 overflow-x-auto",children:s.jsx("pre",{style:{fontSize:"13px",lineHeight:"1.6",fontFamily:'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'},children:s.jsxs("code",{children:[s.jsx("span",{style:{color:"#6b7280"},children:" 1 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"page.click("}),s.jsx("span",{style:{color:"#c3e88d"},children:"'#search-button'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 2 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"time.sleep("}),s.jsx("span",{style:{color:"#f5ab35"},children:"3"}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 3 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"result = page.query_selector("}),s.jsx("span",{style:{color:"#c3e88d"},children:"'#results'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:")"}),` `,s.jsx("span",{style:{color:"#6b7280"},children:" 4 "}),s.jsx("span",{style:{color:"#C3C9D3"},children:"assert "}),s.jsx("span",{style:{color:"#c3e88d"},children:"'Search Results'"}),s.jsx("span",{style:{color:"#C3C9D3"},children:" in result.inner_text()"}),` `]})})})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["Not only is this bad because it will fail if results take more than three seconds —it's also wasting time if the results return in less than three seconds. This is a solved problem in Playwright using the"," ",s.jsx("code",{className:"px-2 py-1 text-sm font-mono text-[#e6e6e6]",children:"wait_for_selector"})," method:"]}),s.jsx("pre",{className:"bg-[#0d0d1a] border border-border/30 rounded-xl p-6 overflow-x-auto my-6",children:s.jsx("code",{className:"text-sm font-mono text-[#e6e6e6]",children:`page.click('#search-button') result = page.wait_for_selector('#results:has-text("Search Results")') assert 'Search Results' in result.inner_text()`})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["Selenium solves this using the"," ",s.jsx("code",{className:"px-2 py-1 text-sm font-mono text-[#e6e6e6]",children:"WebDriverWait"})," class:"]}),s.jsx("pre",{className:"bg-[#0d0d1a] border border-border/30 rounded-xl p-6 overflow-x-auto my-6",children:s.jsx("code",{className:"text-sm font-mono text-[#e6e6e6]",children:`driver.find_element(By.ID, 'search-button').click() result = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, 'search-results')) ) assert 'Search Results' in result.text`})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Flaky tests can also be caused by environmental factors such as:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Unexpected application state from testing in parallel with the same user account"}),s.jsx("li",{children:"Concurrency and race conditions from async operations"}),s.jsx("li",{children:"Increased latency or unreliable service from external APIs"}),s.jsx("li",{children:"Application configuration drift between environments"}),s.jsx("li",{children:"Infrastructure inconsistencies between environments"}),s.jsx("li",{children:"Data persistence layers not being appropriately reset between test runs"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Writing non-flaky tests requires a defensive approach that carefully considers the various environmental conditions under which a test might fail."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-6",children:"What is the difference between brittle and flaky tests?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"A brittle test, while also highly prone to failure, differs from a flaky test as it consistently fails under specific conditions, e.g., if a button's position changes slightly in a screenshot diff test."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Brittle tests can be problematic yet predictable, whereas flaky tests are unreliable as the conditions under which they might pass or fail are variable and indeterminate."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Now that you understand the nature of flaky tests, let's examine a step-by-step process for diagnosing them."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-10 mb-4",children:"Step 1. Gather data"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Before jumping to conclusions as to the cause of the test failure, ensure you have all the data you need, such as:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Video recordings and screenshots"}),s.jsx("li",{children:"Test-runner logs, application and error logs, and other observability data"}),s.jsx("li",{children:"The environment under test and the release/artifact version"}),s.jsx("li",{children:"The test-run trigger, e.g. deployment, infrastructure change, code-merge, scheduled run, or manual"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"You should also be asking yourself questions such as:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Has this test been identified as flaky in the past? If so, is the cause of the flakiness known?"}),s.jsx("li",{children:"Has any downtime or instability been reported for external services?"}),s.jsx("li",{children:"Have there been announcements from QA, DevOps, or Engineering about environment, tooling, application config, or infrastructure changes?"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Having video recordings or frequently taken screenshots is crucial because it's the easiest-to-understand representation of the application state at the time of failure."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/664c9fae-octomind-test-reports.png",alt:"step by step screenshots in Octomind test reports",className:"w-full rounded-xl"}),s.jsx("figcaption",{className:"text-center text-sm text-muted-foreground mt-3",children:"step by step screenshots in Octomind test reports"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Compile this information in a shared document or wiki page that other team members can access, updating it as you continue your investigations. This makes creating an issue or bug report easy, as most of the needed information is already documented."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Now that you've got the data you need, let's begin our initial analysis."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-10 mb-4",children:"Step 2. Analyze logs and diagnostic output"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Effectively utilizing log and reporting data from test runs is essential for determining the cause of a test failure quickly and correctly. Of course, this relies on having the data you need in the first place."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["For example, if you're using Playwright, save the tracing output as an artifact when a test fails. This way, you can use the"," ",s.jsx("a",{href:"https://playwright.dev/docs/trace-viewer",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Playwright Trace Viewer"})," ","to debug your tests."]}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/664c9fae-playwright-trace-viewer.png",alt:"source code in Playwright Trace Viewer tab",className:"w-full rounded-xl"}),s.jsx("figcaption",{className:"text-center text-sm text-muted-foreground mt-3",children:"source code in Playwright Trace Viewer tab"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"To begin your analysis, first identify the errors to determine if the issue stems from one or a combination of the following:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Test code failing unexpectedly (e.g. broken selector/locator)"}),s.jsx("li",{children:"Test code failing as expected (e.g. failed assertion)"}),s.jsx("li",{children:"Application error causing incorrect state or behavior (e.g. JavaScript exception, rejected promise, or unexpected backend API error)"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"The best indication of a flaky test is when the application seems correct, yet a failure has occurred for no apparent reason. If this type of failure has been observed before, but the cause was never resolved, the test is probably flaky."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Things become more complicated to diagnose when the application functions correctly, yet the state is clearly incorrect. You'll then need to determine if the test relies on database updates or responses from external services to confirm if infrastructure or data persistence layers could be the root cause. Hopefully, your application-level logs, errors, or exceptions will provide some clues."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Debugging test failures is easier when all possible data is available, which is why video recordings, screenshots, and tools such as Playwright's Trace Viewer are so valuable. They help you observe the system at each stage of the test run, giving you valuable context as to the application's state leading up to the failure. So, if you're finding it challenging to diagnose flaky tests, it could be because you don't have access to the right data."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"If the test has been confirmed as flaky, document how you came to this conclusion, what the cause is, and, if known, how it could be fixed. Then, share your findings with your team."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Because you've confirmed the test is flaky, re-run your tests, and with any luck, they'll pass the second or third time around. But if they continue to fail, or you're still unsure why the test is flaky, more investigation is required."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-10 mb-4",children:"Step 3. Review recent code changes"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"If the test run was triggered by an application or test code changes, review the commits to look for updates that may be causing the tests to fail. For example, newly added tests that aren't cleaning up their state executing before the now-failing test."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Also, check for changes to application dependencies and configuration."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-10 mb-4",children:"Step 4. Verify the environment"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"If your flaky tests still fail after multiple re-runs, it's likely that application config, infrastructure changes, or third-party services are responsible. Test failures caused by environmental inconsistencies and application drift can be tricky to diagnose, so check with teammates to see if this kind of failure has been seen before under specific conditions, e.g. database server upgrade."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["Running tests locally to step-debug failures or in an on-demand isolated environment is the easiest way to isolate which part of the system may be causing the failure. We have open sourced a tool to do exactly that - our Debugtopus. Check it out in our docs or go directly to the"," ",s.jsx("a",{href:"https://github.com/OctoMind-dev/debugtopus",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Debugtopus repo"}),"."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Also, check for updates to testing infrastructure, such as changes to system dependencies, testing framework version, and browser configurations."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-6",children:"Reducing flaky tests"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"While this deserves a blog in its own right, a good start to preventing flaky tests is to:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-1 mb-4",children:[s.jsx("li",{children:"Ensure each test runs independently and does not depend implicitly on the state left by the previous tests."}),s.jsx("li",{children:"Use different user accounts if running tests in parallel."}),s.jsx("li",{children:"Avoid hardcoded timeouts by using waiting mechanisms that can assert the required state exists before proceeding."}),s.jsx("li",{children:"Ensure infrastructure and application configuration across local development, testing, and production remains consistent."}),s.jsx("li",{children:"Prioritize the fixing of newly identified flaky tests as fast as possible."}),s.jsx("li",{children:"Identify technical test code debt and pay it down regularly."}),s.jsx("li",{children:"Promote best practices for test code development."}),s.jsx("li",{children:"Add code checks and linters to catch common problems."}),s.jsx("li",{children:"Require code reviews and approvals before merging test code."})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-6",children:"Conclusion"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"The effort required to diagnose flaky tests properly and document their root cause is time well spent. I hope you're now better equipped to diagnose flaky tests and have some new tricks for preventing them from happening in the future."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"We constantly fortify Octomind tests with interventions to prevent flakiness. We deploy active interaction timing to handle varying response times and follow best practices for test generation already today. We are also looking into using AI to fight flaky tests. AI based analysis of unexpected circumstances could help handling temporary pop-ups, toasts and similar stuff that often break a test."})]}),s.jsx("div",{className:"mt-16",children:s.jsx(Ee,{name:"Maximilian Link",title:"Senior Engineer at Octomind",imageSrc:"/assets/blogs/maxi-closeup.webp"})}),s.jsx(ge,{})]})}),s.jsx(se,{})]})}function pR(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-3xl",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-12",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-4 lowercase",children:"octomind raises $4.8 m to reinvent testing with AI"}),s.jsx("p",{className:"text-xl text-muted-foreground",children:"Led by Cherry Ventures, the round supports the developer tool's ambitions to create a bug-free future for AI-assisted software development and testing"})]}),s.jsx("div",{className:"mb-12 rounded-xl overflow-hidden",children:s.jsx("img",{src:"/assets/blogs/66152200e6cc761cf82573b7_A7309500.webp",alt:"octomind team picture, April 2024",className:"w-full"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none",children:[s.jsx("h3",{className:"text-xl font-bold text-white mt-10 mb-4",children:"Hey everyone,"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["We've got some pretty exciting news to share with the Octomind community today! Just six months after stepping out into the light from our stealth mode, we've successfully"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"raised $4.8 million in seed capital"}),". This funding round, led by the visionaries at"," ",s.jsx("a",{href:"https://www.cherry.vc/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Cherry Ventures"})," ","and supported by a stellar lineup of angels like"," ",s.jsx("a",{href:"https://www.linkedin.com/in/seanmullaney/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Sean Mullaney from Algolia"}),","," ",s.jsx("a",{href:"https://www.linkedin.com/in/charlessonghurst/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Charlie Songhurst"}),", and"," ",s.jsx("a",{href:"https://www.linkedin.com/in/lutzfinger/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Lutz Finger"}),", is a huge vote of confidence in our mission to reshape end-to-end testing for the better."]}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 italic text-[#15D7AB]",children:["Find the full list of investors"," ",s.jsx("a",{href:"/about",className:"underline",children:"here"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"In a world where AI is becoming increasingly central to web development, boosting productivity but also, frankly, sometimes dropping the ball on code quality, the need for tools like ours has never been more critical. It's become clear that while AI can turbocharge the coding process, ensuring the quality of the output is another challenge altogether. And that's precisely where Octomind steps in."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["Our founders,"," ",s.jsx("a",{href:"https://www.linkedin.com/in/marcmengler/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Marc Mengler"})," ","and"," ",s.jsx("a",{href:"https://www.linkedin.com/in/daniel-roedler/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Daniel Roedler"}),", brought Octomind to life out of a shared frustration with the status quo in software testing. They've spent years in the trenches, building large applications that need to run without breaking, and saw firsthand the drag inefficient testing can have on innovation and development speed. With Octomind, they envisioned a solution that leverages AI to ensure any application works as it's supposed to do."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"This fresh injection of capital will be pivotal in enhancing our AI's ability to understand and navigate web applications, identifying and testing critical user flows with precision. We're not just talking about slapping together some code; we're about creating a seamless, bug-free user experience that stands the test of real-world application."}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-muted-foreground",children:[s.jsx("span",{className:"italic text-[#15D7AB]",children:`"Octomind addresses one of the world's largest ($38B) and least automated markets that can be augmented by AI. Today, software testing is the single most hated task for developers. Yet, more and more CTOs expect devs to do the testing themselves, which grows the total market to $184B worldwide. Thanks to their impressive experience and well-advanced AI agent product, I believe Octomind will make developers love software testing soon,"`})," ","said"," ",s.jsx("a",{href:"https://www.linkedin.com/in/jmasemann/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Jasper Masemann"}),", partner at Cherry Ventures"]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Our ultimate goal? A world where software testing isn't a dreaded task but a seamless part of the development process, thanks to AI. We believe in a future where developers can focus on what they do best - building incredible software - while leaving the tedious task of testing to an intelligent system capable of auto-fixing bugs as they arise."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"This funding is a big step forward in our journey, but we're just getting started. We're excited to deepen our product's capabilities and, most importantly, continue working closely with our community to refine and enhance our solution. After all, it's your insights and feedback that help us make Octomind the best it can be."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Thanks for being part of this adventure. Here's to building a future where software testing is smarter, faster, and just plain better."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"With ❤ from the Octomind team"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const xR=()=>s.jsxs("div",{className:"min-h-screen bg-[#13142B] text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-[#00AF93]/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-purple-light/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 text-[#00AF93] lowercase animate-fade-in-up",children:"the full-stack testing mindset"}),s.jsx("p",{className:"text-xl text-[#C3C9D3] mb-8 animate-fade-in-up",children:"Working end-to-end tests are in everyone’s interest, whether you are on frontend, backend, engineering or QA. A call for collaborative effort."}),s.jsx("div",{className:"mb-12 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/66053bc07722f3570e719067_coder-duo-clean.webp",alt:"Two developers collaborating",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none animate-fade-in-up",children:[s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8",children:[s.jsx("span",{className:"italic text-octo-teal",children:'"I love contributing to and maintaining our end-to-end test suite"'})," ","- Nobody, Ever"]}),s.jsx("h2",{className:"text-xl font-bold text-[#00AF93] mb-4",children:"TL;DR"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"If you're pushed for time, here are the main takeaways:"}),s.jsxs("ul",{className:"space-y-4 text-[#C3C9D3] list-disc pl-6",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Adopt a Full-Stack Testing Mindset"}),s.jsx("br",{}),"Unless you're an API-only company, backend systems don't operate in isolation. Pay special attention to the integration points between different backend services, especially when async operations and task queues are involved."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"The Importance of Simulating Real-World Conditions"}),s.jsx("br",{}),"Include tests that simulate unpredictable real-world conditions, such as variable network latency, high traffic loads, and interactions with external services. These conditions can expose backend issues that are not apparent in a controlled test environment."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Developers: Collaborate with and Educate QA Teams"}),s.jsx("br",{}),"Work closely with QA automation engineers to understand the limitations and deficiencies in the current end-to-end testing codebase and guide them in upskilling and improving their ability to write comprehensive and robust tests."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"QA: Proactively Learn from Developers"}),s.jsx("br",{}),"QA should take every opportunity to learn how data flows from the front-end to the backend and other architectural and infrastructure concerns so you can start to troubleshoot and test with the mindset of a developer."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Consider Playwright for Boosting Speed and Productivity"}),s.jsx("br",{}),"If you haven't already, look into switching to Playwright, as that will likely offer the biggest bang for your buck for simplifying the test code, shrinking test times in CI, and reducing the flakiness of tests overall."]})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8 mt-8",children:"End-to-end tests have a poor reputation for being brittle, slow, and frustrating for both QA and engineering teams to manage."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"My goal with this article is to surface the often overlooked role end-to-end testing can play in uncovering types of backend bugs that are difficult, if not impossible, to find with integration or unit tests alone and why the solution lies in both software engineers and QA adopting a full-stack testing mindset."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"And don't worry - this isn't a thinly veiled attempt to position our product as the answer to all your end-to-end testing woes. I simply want to inspire you to write better code that provides a more robust customer experience by embracing the value E2E testing can offer."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Why developers should care about e2e tests"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Most developers consider their job done once unit (and hopefully, integration tests) have been written. End-to-end tests are usually an afterthought for developers, if they're thought of at all."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Customer success is not whether your backend tests pass and the APIs under test fulfil their contract - it's whether an entire customer flow works as expected in a production setting."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["There are many real world examples to illustrate the importance of end-to-end tests to identify backend issues."," ",s.jsx("strong",{className:"text-octo-purple-light",children:"Just recently, we would have shipped a broken settings feature without E2E tests."})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"The backend test suite passed, but the Auth API call was incorrectly restricted to admin users only, and because every developer has admin permissions, the issue was invisible to us."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Luckily, we had an end-to-end test fail as it used a real customer account with standard permissions, revealing the underlying issue. Without this test, the user flow would have been broken on release for our users to discover."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The organizational divide between engineering and QA"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"This is typically how organizations think of end-to-end testing:"}),s.jsx("div",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/660428682de3c526116cb466_testing-responsibilities.png",alt:"developers vs. QA - testing responsibilities within an organization",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Dividing test design and responsibility solely according to an org chart makes logical sense but doesn't work well in practice."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["While a poor-quality or brittle end-to-end test suite with constant failures frustrates everyone,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"it's ultimately developers who pay the price"})," as they will most likely be responsible for investigating each failure and determining the solution. It's therefore in their best interests to ensure E2E tests provide maximum value with minimal false positives."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"While I'm not suggesting developers save the day and take ownership of end-to-end testing, they need to play a leadership role to drive improvements in test quality by:"}),s.jsxs("ul",{className:"text-[#C3C9D3] list-disc pl-6 space-y-4",children:[s.jsxs("li",{children:["Understanding the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"limitations of unit and integration tests"})," for simulating real-world scenarios, mainly where external services are involved."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Working closely with QA automation engineers"})," to educate them about flows where async task processing or data synchronization operations may create a more nuanced happy path."]}),s.jsxs("li",{children:["Reviewing test cases to ensure best practices are being followed,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"ensuring test code is easily maintainable, readable and debuggable"}),"."]}),s.jsxs("li",{children:["Encouraging and"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"helping QA learn more about the system architecture"}),", infrastructure, and application code so they may learn how to think like a developer and write more sophisticated and comprehensive test cases."]})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8 mt-8",children:"The goal is for QA and development teams to act as collaborative partners and take greater ownership of the quality and reliability of their end-to-end tests together. This helps foster a better team-oriented environment and puts the customer experience and code quality as the shared objective."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Ultimately, the best developers are the ones that don't just focus on addressing the strict requirements of their user story or task, but care deeply about the quality and delivered customer value and don't let organizational boundaries limit the scope of their testing."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The e2e testing landscape is transforming"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"There's never been a better time to be optimistic about end-to-end testing improving, thanks to Microsoft's open-source testing framework, Playwright, and automated testing fixes powered by AI."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["Since Playwright's release in 2020, it's racked up more than 60k stars on GitHub and over 4.5 million installs per week from npm — and with good reason, as it's the most modern and forward-thinking test framework."," ",s.jsx("a",{href:"/blog/why-we-built-an-e2e-testing-tool-on-top-of-playwright",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"We're betting our entire company on it"}),"."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"It's blazingly fast, tests multiple browsers in parallel, runs headed and headless, has flexible layout-based selectors such as near, below, and above, flakiness prevention features such as auto-wait, integrated VS Code debugging, and much more."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"This means writing high quality and reliable E2E tests doesn't require years of experience and weird timing hacks. And the fact it's used and owned by Microsoft means that you can be sure it's here to stay and will continue to be invested in for the long term."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Where AI fills the gaps"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"But Playwright alone doesn't take all the pain away. You still have to identify, write, run, and maintain your Playwright E2E tests."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"At Octomind, we think that AI has the capacity to make that part much easier. Our LLM based agent is trained to have a semantic understanding of code at the UI layer, enabling them to infer relationships between related UI components and make intelligent suggested changes to fix failures caused by issues such as broken Playwright locators."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"We want to remove the needless interruptions caused by failing UI tests that aren't genuine regressions by automatically fixing them, e.g. when link text is updated or a button position changes. It's a task AI is well suited to."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Summary"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-8",children:["End-to-end testing isn't just for testing the front-end and catching simple UI regressions and the"," ",s.jsx("a",{href:"/blog/testing-pyramid-an-evolutionary-tale",rel:"noopener noreferrer",className:"underline",children:"testing pyramid is changing"})," ","to reflect the value they can offer."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"End-to-end tests are unique in that they can simulate real-world conditions such as network latency, high traffic loads, and interactions with external services. These conditions can expose backend issues unlikely to surface in controlled environments such as those where integration and unit tests are run."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"By adopting a collaborative and comprehensive approach to end-to-end testing where developers support QA in developing a deeper understanding of the entire application stack, software teams can significantly improve the quality of their code and provide a more robust and reliable customer experience."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"With Playwright and AI revolutionizing end-to-end testing, there's never been a better time to adopt a 'Full-Stack Testing Mindset' to get the most out of your E2E test suite."})]}),s.jsx(Ee,{name:"Daniel Draper",title:"Lead Engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),fR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold mb-6 text-octo-teal",children:"why we built an e2e testing tool on top of Playwright"}),s.jsx("p",{className:"animate-fade-in-up text-xl text-muted-foreground mb-8",children:"To build a better way of testing we needed a testing framework to complement our AI agents. Our ideal end-to-end testing tool would help build high quality apps fast."}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/65e7155616a5db17d34ab1ab_playwright-octopi.webp",alt:"Playwright octopi illustration",className:"w-full rounded-lg mb-12"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"When we started Octomind, we wanted to build a better way of testing for ourselves and our community. Our ideal end-to-end testing tool would achieve three things:"}),s.jsxs("ol",{className:"list-decimal pl-6 space-y-2 mb-6",children:[s.jsx("li",{children:"Help build high-quality web apps, fast"}),s.jsx("li",{children:"Eliminate the pain of writing and maintaining e2e tests"}),s.jsx("li",{children:"Use AI to make it all happen"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Right at the beginning, we had to make a critical choice - we could either write a new test automation framework or build our tooling on top of an established e2e testing library."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Why we didn't build a test framework"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Wasting developer hours for building a framework while there are open source options on the market that have broad adoption sounded like a serious case of reinventing the wheel."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We decided to spend our time and energy on what we do best: AI."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The pickup of Playwright tests"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:['Microsoft launched Playwright in January 2020 as an "End-to-end testing tool for modern websites". It has become a major player in E2E testing in this short time: its'," ",s.jsx("a",{href:"https://github.com/microsoft/playwright",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"GitHub repository"})," ","has 59.2k stars and 3.3k forks as of February 2024, while weekly"," ",s.jsx("a",{href:"https://npmjs.com/package/playwright",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"NPM downloads"})," ","average 3.3 million. These numbers surpass"," ",s.jsx("a",{href:"https://cypress.io/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Cypress"}),", the other leader in E2E testing and"," ",s.jsx("a",{href:"https://selenium.dev/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Selenium"}),", the OG test automation framework."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Despite its newness and relatively small community, Playwright's evangelists were loud enough to trigger an avalanche. Drop by any dev forum and you'll find lively discussions on whether Playwright is better than Cypress (mostly yes) or whether to migrate from Cypress to Playwright (not necessarily). One area of consensus? Playwright is the best choice for new projects, for reasons I'll get into later."}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/cory_house_on_twitter.png",alt:"cory house backlog twitter",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center",children:["source:"," ",s.jsx("a",{href:"https://x.com/housecor/status/1597954687705104385",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Cory House on X"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4 mt-6",children:"Playwright is supported by the developer community. Playwright is open source and free to use (so far). And an important point for us - Playwright is here to stay."}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8 text-xl",children:s.jsx("span",{className:"text-octo-teal",children:`"With Playwright being owned by Microsoft, there are still unknowns but when I look at the tooling they have built and provided for the developer community, I would put my bet on Microsoft. When I look at Typescript, VS Code, .Net Core, etc, I don't have many fears about the future of Playwright."`})}),s.jsx("p",{className:"text-muted-foreground mb-8",children:s.jsx("a",{href:"https://playwrightsolutions.com/playwright-ask-me-anything-debrief-will-playwright-replace-cypress/",target:"_blank",children:s.jsx("strong",{className:"text-octo-purple-light underline",children:"Butch Mayhew"})})}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Why Playwright is the best foundation for Octomind"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"But at the end of the day, the technology needs to live up to the hype. Here are some other reasons we chose Playwright:"}),s.jsxs("ul",{className:"list-disc pl-6 space-y-4 mb-6",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"State-of-the-art testing for modern browsers:"})," ","Playwright supports today's browser architecture and user behaviors, allowing testing across domains, browser tabs, origins, and users. You can also include multiple test scenarios in a single test, decreasing testing time."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Cross-everything support:"})," It offers cross-browser testing (all modern rendering engines including Chromium, WebKit, and Firefox), cross-platform testing (Windows, Linux, and macOS, locally or on CI, headless or headed), and cross-language support (TypeScript, JavaScript, Python, .NET, and Java)."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Speed:"})," The last thing anyone wants to do is waiting for their test suites to run. Playwright offers fast test execution speeds plus parallel testing."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Flakiness prevention:"})," Playwright offers resiliency-building features like auto-wait, web-first assertions, and tracing to fight flaky tests. Still not on the level we'd like to have it, but it has some tricks to increase the confidence in the test outcomes."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:s.jsx("a",{href:"https://playwright.dev/docs/trace-viewer-intro",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Playwright trace viewer"})})," ","- a GUI tool, which lets you play back visual recordings of your tests to pinpoint where specific interactions occurred. A functionality we adopted in our test reports almost from the beginning."]})]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The 'what if'"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We are big Playwright believers, but we've built our tooling in a way that makes it possible to expand the framework coverage in the future. If our users and the market need us to."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Every ever so good technology can find its challenger. Software engineers testing their apps are also not a monolith, they have different needs and preferences. Different frameworks fit different use cases. That's why we want to keep real Octomind open to a real possibility of working on top of other testing frameworks."})]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CTO / CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),gR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold mb-6 text-octo-teal",children:"keep your Copilot and your code quality"}),s.jsx("p",{className:"animate-fade-in-up text-xl text-muted-foreground mb-8",children:"Degrading code quality when using AI coding assistants will undermine delivery speed and neutralize the time they save. How do we get quality back on track?"}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/65c90563ef0c29c770b57ee1_saving-octo.webp",alt:"Octopus saving code quality illustration",className:"w-full rounded-lg mb-12"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"AI generated issues with code quality"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Studies on GitHub Copilot's impact on coding trends reveal a paradox: while it boosts coding speed it also introduces problems with code quality and maintainability. The latest study '"," ",s.jsx("a",{href:"https://gitclear.com/coding_on_copilot_data_shows_ais_downward_pressure_on_code_quality",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Coding on Copilot: 2023 Data Suggests Downward Pressure on Code Quality"}),"' by GitClear confirms the pattern. According to"," ",s.jsx("a",{href:"https://github.blog/2022-09-07-research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:`GitHub's own study, developers write code "55% faster"`})," ","when using Copilot, but there appears to be a decline in code quality and maintainability of the code generated by AI."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Analyzing over 153 million changed lines of code, the GitClear study predicts doubled code churn in 2024 compared to 2021 and a dip in effective code reuse. For more insights resulting from the study, check out"," ",s.jsx("a",{href:"https://visualstudiomagazine.com/articles/2024/01/25/copilot-research.aspx",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"this summary"})," ","written by David Ramel or watch"," ",s.jsx("a",{href:"https://youtu.be/7ktvyqvWkiU",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Theo Browne's reaction"})," ","to it."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["These findings raise important questions about the long-term impacts of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AI tools in software development"}),". Poor code leads to app failures, revenue loss, and skyrocketing cost to fix the mess."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"AI testing and refactoring to course correct AI generated code"}),s.jsx("blockquote",{className:"border-l-4 border-white pl-6 italic my-8 text-xl",children:s.jsx("span",{className:"text-[#15D7AB]",children:"How do we get quality back on track while keeping the productivity benefits of tools like Copilot? After all, degrading quality will undermine delivery speed and neutralize some of the gains, if not most."})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"AI might be the answer here, too."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"First off, we need to push AI models to churn out code that meets quality standards. This relies heavily on Copilot-type tool providers stepping up their game. It's reasonable to assume they'll get better at solving it. However, it's not a silver bullet."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Second, new tools will make quality control, debugging and refactoring as easy as AI code creation. Startups tackling non-trivial developer problems around code quality mushroomed in the past 2 years. Exponentially so since the launch of GPT4. With decreasing code quality, they will become a necessity. But they will take time to learn and use which again, offsets the time you saved using AI generated code."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"A third addition will be the generation of test code at all levels. Copilot itself seems to be decent in suggesting helpful unit tests already. But it won't be enough."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/reddit-ai-code-comment.png",alt:"A reddit quote on Copilot",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-muted-foreground text-sm mt-2 text-center",children:"a Redditor commenting on GitHub Copilot's code & test writing ability"})]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The case for AI in UI testing"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Having AI taking care of end-to-end tests is a more complex task to solve. It is also essential not only to produce code fast but to also get it deployed fast. The speed gains from code generation won't materialize otherwise."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Direct test code generation is still not there yet, as many evaluations seem to point out, like"," ",s.jsx("a",{href:"https://youtube.com/live/8kxskV4bYDE?feature=shared&t=1033",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"this analysis"})," ","of gpt 3.5 and gpt4 capability of handling e2e (Cypress & Playwright) tests."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["But what if we approached it differently and used AI for its"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"commonsense knowledge"}),"? Think UI tests as functional e2e tests for front-end. They test for functionality based on user interactions. AI can have a look at an application and interact with it as prompted. This way AI doesn't mimic the coder but the user. LLMs combined with vision models show a promising route to handle the problem."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"AI testing and trust"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The biggest challenge with any test suite and especially with end-to-end tests, is trust. If they don't provide enough coverage, they are not trustworthy. The same applies if they are flaky. Moreover, they need to run reasonably fast because otherwise, it is a pain to use them. Adding AI to the equation introduces even more reliability concerns."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The AI needs to provide high enough coverage by producing test cases of high quality and low flakiness which are running fast enough for a short feedback cycle. It has to be able to auto-fix broken tests to achieve meaningful coverage and keep it long term."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"To counteract the diminishing trust into AI generated code, an efficient and trustworthy AI test strategy can go a long way."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's what we are working on."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["We are out with"," ",s.jsx("a",{href:"https://app.octomind.dev/setup/url",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Octomind's beta version"})," ","but we need your feedback to nail an AI testing tool you will trust and love to use."]})]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CTO / CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),vR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 text-octo-teal animate-fade-in-up",children:"We went viral with a broken app"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8 animate-fade-in-up",children:"Post mortem on a weekend of missed opportunity. And a lesson learned on moving fast and making sure you don't break things."}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/657b614e36bb1b5c79216628_burning-octo-1200.png",alt:"Burning octopus at computer illustration",className:"w-full rounded-lg mb-12"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Last Friday, we went viral on"," ",s.jsx("a",{href:"https://reddit.com/r/programming/comments/18dprj9/never_ship_on_fridays/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Reddit (500K views, 200 comments)"})," ","with a blog called"," ",s.jsx("a",{href:"/blog/on-developer-dogma-3-never-ship-on-fridays",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Never ship on Fridays"}),". It was one of those articles our developers write to get their engineering rant urges out of the system."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It went viral with all the usual clash of opinions, corrections, eye-rolls and gotcha! moments. We loved the whole pandemonium. We were so grateful for everyone who contributed, who read the blog, grateful for tons of insightful discussions and even for the smart-asses."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Some stayed to check out our product. They didn't get very far, though. The setup process for the first UI tests was broken. ",s.jsx("strong",{className:"text-octo-purple-light",children:"Ouch."})]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What (probably) went wrong"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["During the"," ",s.jsx("a",{href:"https://app.octomind.dev/",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"user sign-up process we generate real e2e tests"}),", so there's multiple browsers running in the cloud taking care of discovering those test cases, by parsing the html and passing it to our LLM-backed AI agent."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["The more people sign-up the more load this causes on our backend servers running in the cloud. We have auto-scaling in place, but at some point, the good old"," ",s.jsx("a",{href:"https://en.wikipedia.org/wiki/Slashdot_effect",className:"underline",target:"_blank",rel:"noopener noreferrer",children:'"reddit hug of death"'})," ","caught up to us and brought our backend to its knees."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This should have not affected our sign-up process of course, but the 2nd step of the signup (taking a screenshot of your actual app in a real browser) is currently run on the same service. We knew it was going to cause scaling problems eventually, but in a startup, you make trade-offs."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"When end-to-end tests on production make sense"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"While we have a great testing coverage by unit and e2e tests (as a testing product company 🙂), we didn't run them regularly on production. We have since remedied this and are dog-fooding ourselves continuously, not just in our development process but also production. We will be alerted when the sign-up breaks again and can scale our backend manually."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Moreover, we will work sooner on disentangling our scaling and infrastructure than originally planned. Which brings us to:"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We try not to release on Fridays. Maybe we shouldn't post blogs either."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"May you and your web app stay hydrated!"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:s.jsx("strong",{className:"text-octo-purple-light",children:"Octoneers"})})]}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),yR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-24",children:s.jsxs("article",{className:"container max-w-3xl mx-auto px-4",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6 animate-fade-in-up",children:"test your code! #4"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-12 animate-fade-in-up",children:"Software testing is littered with strong opinions and i'm no different. About the one dogma I'm guilty of - testing what you've built."}),s.jsx("div",{className:"mb-12 animate-fade-in-up",children:s.jsx("img",{src:"/assets/blogs/656f2d9457d5c3ccb9f8aa2b_preacher-octo-1200.jpg",alt:"Preacher octopus illustration",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert prose-lg max-w-none",children:[s.jsxs("p",{className:"text-foreground/90 leading-relaxed mb-6",children:["I have long felt the urge to address dogmatism in software engineering. As opinions have seemed to get more radical in developer communities, I had to add my 2 cents to a few of them. I pointed to some of the biggest beefs in the developer community - like"," ",s.jsx("a",{href:"/blog/deconstructing-popular-developer-dogmas-1",children:"unpopular design patterns"}),","," ",s.jsx("a",{href:"/blog/on-type-safety-in-langchain-ts",children:"typescript"})," or"," ",s.jsx("a",{href:"/blog/on-developer-dogma-3-never-ship-on-fridays",children:"releasing on Fridays"})," and tried to add some nuance to the discussions. I could finally sleep safe and sound again."]}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:`Until someone suggested writing an additional piece on testing. "What about addressing the 'abolish unit tests' clash?"`}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"🤯"}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"The TDD fan in me got triggered. I can understand people's reluctance to use TDD always and foremost, but abolishing tests is one step (way!) too far."}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"There it was - my very own dogma. I'm far from being alone, software testing is littered with very strong opinions."}),s.jsx("h2",{className:"text-2xl font-bold text-foreground mt-12 mb-6",children:"Unit tests defenders vs. abolitionists"}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"The unit testing squabble is one of the most controversial conversations in the community. Sometimes leading to hilarious exchanges like this one:"}),s.jsx("div",{className:"my-8 aspect-video",children:s.jsx("iframe",{className:"w-full h-full rounded-lg",src:"https://youtube.com/embed/pvBHyip4peo",title:"ThePrimeagen on Unit Testing",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0})}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"To be fair, there are some legit arguments against unit testing:"}),s.jsxs("ul",{className:"space-y-4 mb-8 list-disc pl-6",children:[s.jsxs("li",{className:"text-foreground/90",children:["Unit tests can potentially, if done wrongly, lead to"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"unnecessarily fragmented and complex code"}),". The emphasis on modularity and isolation can increase cognitive complexity and make it difficult to understand the code as a whole​​. The problem of how to make code testable is not trivial."]}),s.jsxs("li",{className:"text-foreground/90",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"$$$"})," Although being small and isolated, and being cheaper than literally any other kind of test, they are still not free. They can become complex and time-consuming, especially when starting out with untested legacy code, where each unit test requires mocking half the codebase."]}),s.jsxs("li",{className:"text-foreground/90",children:["Unit tests often ",s.jsx("strong",{className:"text-octo-purple-light",children:" rely on implementation specifics"}),", particularly when using mock-based testing. This leads to coupling issues, making refactoring more expensive and discouraging devs from making those necessary changes​​ quite as freely."]}),s.jsxs("li",{className:"text-foreground/90",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Real-world testing."})," They often miss out on testing how different parts of the software interact in a real-world scenario​​. In the end what counts is that the code does what it's supposed to in the customer's context and not some isolated mocked unit test environment."]})]}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/unit-integration-test-meme.jpeg",alt:"2 unit tests 0 integration tests meme",className:"mx-auto rounded-lg"}),s.jsx("figcaption",{className:"text-muted-foreground text-sm mt-2 text-center",children:"unit test vs. integration tests meme"})]}),s.jsx("ul",{className:"space-y-4 mb-8 list-disc pl-6",children:s.jsxs("li",{className:"text-foreground/90",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Overemphasis of their role"})," at the cost of other test types. People might overlook the value of testing at higher levels, which reflect real user interactions better."]})}),s.jsxs("p",{className:"text-foreground/90 leading-relaxed mb-6",children:["In my opinion however, most of the criticism boils down to the wrong application of unit tests. Unit tests are often misunderstood and misapplied. For example, they don't necessarily need to focus on a single file, object, or function, but rather on a ",s.jsx("span",{className:"text-octo-teal italic",children:"unit"})," of behavior, which can be more broadly defined​​."]}),s.jsxs("p",{className:"text-foreground/90 leading-relaxed mb-6",children:["I just think the ",s.jsx("strong",{className:"text-octo-purple-light",children:"arguments for unit testing"})," are too solid to be ignored:"]}),s.jsxs("ul",{className:"space-y-4 mb-8 list-disc pl-6",children:[s.jsxs("li",{className:"text-foreground/90",children:["They ",s.jsx("strong",{className:"text-octo-purple-light",children:"identify bugs early"})," before the issue snowballs into a larger problem as they are the first thing run before your code even hits any testing environment."]}),s.jsxs("li",{className:"text-foreground/90",children:["They help immensely with ",s.jsx("strong",{className:"text-octo-purple-light",children:"code refactoring"}),". They make sure new code still performs as intended and that existing functionality is not broken by the changes. Running them continuously while changing the underlying implementation of the unit is the only way I feel confident enough in my own changes."]}),s.jsxs("li",{className:"text-foreground/90",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Documentation."})," Unless you're a lonesome cowboy dev, someone else",s.jsx("span",{className:"text-octo-teal italic",children:"will"})," look into your code at some point. Unit tests document how your code is supposed to be used. Great thing if someone else needs to wrap their head around it, or see the intended API usage of your piece of code."]})]}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/octo-cowboy-1200.jpg",alt:"Lonesome cowboy octopus developer",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-muted-foreground text-sm mt-2 text-center",children:"Lonesome cowboy dev whose code is none of your business"})]}),s.jsxs("ul",{className:"space-y-4 mb-8 list-disc pl-6",children:[s.jsxs("li",{className:"text-foreground/90",children:["Unit tests are the ",s.jsx("strong",{className:"text-octo-purple-light",children:"fastest in the testing town"}),". When applied correctly they"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"speed up the entire development process"}),'. By catching bugs early and simplifying debugging, they reduce the overall time spent on troubleshooting and will in the long run absolutely make your lead time to changes go down as you will have the confidence to "just release".']}),s.jsxs("li",{className:"text-foreground/90",children:["They ",s.jsx("strong",{className:"text-octo-purple-light",children:"improve code quality"}),". They force devs to write more modular, maintainable, and understandable code. Of course this only works if you treat unit test code as just as important as your production code - it should be part of your review process and standards enforced just the same."]})]}),s.jsx("h2",{className:"text-2xl font-bold text-foreground mt-12 mb-6",children:"Testing drill sergeants vs. let's (maybe) test in production"}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"The testing debate continues around the importance of tests in general. A small, vocal group of 'move fast, break things' devs advocate for releasing without testing, testing in production or even leaving the quality check to the users. I guess you can, if the stakes are low enough and your users are forgiving."}),s.jsxs("figure",{className:"my-8",children:[s.jsx("img",{src:"/assets/blogs/testing-in-production-meme.jpg",alt:"Testing in production meme",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-muted-foreground text-sm mt-2 text-center",children:"testing in production meme"})]}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:`For me, this was the hardest part of questioning my own dogma. Ask any of my current or former colleagues, there's one thing that I have been pretty dogmatic about in my career - testing your code. I've been likened to a "testing drill sergeant".`}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"I don't think I'm quite as strongly opinionated about this anymore as I used to be. I don't really care what level of the testing pyramid you choose to cover your code. I also am fine with tests growing along with the code: a prototype probably doesn't need full coverage, while the full scalable production ready app does and I do think writing good tests is just as hard as writing good code (maybe even harder), so I understand the struggle."}),s.jsxs("p",{className:"text-foreground/90 leading-relaxed mb-6",children:["If I'm allowed one ultimate developer opinion, please,"," ",s.jsx("span",{className:"text-octo-purple-light",children:"test your code"}),". It doesn't have to suck. We try to help by making testing easier, so feel free to see if that can assist you achieving the code quality you want."]}),s.jsx("hr",{className:"border-border my-12"}),s.jsxs("p",{className:"text-foreground/90 leading-relaxed mb-6",children:["Thanks for going through reading my 2 cents about dogmatism in software engineering. Let me wrap up my dogma busting with one last thought from the"," ",s.jsx("a",{href:"https://x.com/GergelyOrosz",target:"_blank",className:"underline",children:"Pragmatic Engineer"}),":"]}),s.jsx("figure",{className:"my-8",children:s.jsx("img",{src:"/assets/blogs/gergely-quote.png",alt:"Gergely Orosz quote on Twitter",className:"rounded-lg mx-auto"})}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"So take it all with a grain of salt!"}),s.jsx("p",{className:"text-foreground/90 leading-relaxed mb-6",children:"If you think I'm missing something or just want to chat about dogmatism, tests, AI or general startup engineering, you can find me here:"}),s.jsxs("ul",{className:"space-y-2 mb-8 list-disc pl-6",children:[s.jsx("li",{className:"text-foreground/90",children:s.jsx("a",{href:"https://twitter.com/Germandrummer92",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"@Germandrummer92 on X"})}),s.jsx("li",{className:"text-foreground/90",children:s.jsx("a",{href:"https://github.com/Germandrummer92",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"@Germandrummer92 on github"})}),s.jsx("li",{className:"text-foreground/90",children:s.jsx("a",{href:"https://discord.gg/3ShnZMKRfA",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"@daniel.draper on the octomind discord"})})]})]}),s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),bR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("article",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("div",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsxs("div",{className:"animate-fade-in-up mb-8",children:[s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"never ship on fridays #3"}),s.jsx("p",{className:"text-xl text-muted-foreground",children:"Since we created Octomind we've released 64 times out of which 9 were on Fridays. We don't refuse it, but we value our developers' time off. My take #3 on a popular developer dogma."})]}),s.jsxs("div",{className:"animate-fade-in-up mb-12",children:[s.jsx("img",{src:"/assets/blogs/654b9dccfc95e0be12246fcb_sleepy-octo-1200.png",alt:"an octoneer enjoying its weekend",className:"w-full rounded-lg"}),s.jsx("p",{className:"text-sm text-muted-foreground text-center mt-2",children:"an octoneer enjoying its weekend"})]}),s.jsxs("div",{className:"animate-fade-in-up prose prose-lg prose-invert max-w-none",children:[s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["There is a strong opinion in software development about ",s.jsx("em",{className:"text-[#15D7AB]",children:"NOT"})," releasing on Fridays. Friday deadlines (often the end of the work week or sprint) are pushing developers to rush to finish tasks. The temptation to push deployments through without adequate checks is high. What can go wrong?"]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Compromised quality"}),": A tendency to bypass proper testing or ignore test failures leads to unstable releases."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Weekend recovery"}),": If something goes wrong, devs have to give up their weekend to fix the issues, leading to burnout and dissatisfaction."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Lost context"}),": Developers who have shifted focus may not recall all the details. Troubleshooting gets more difficult."]}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsxs("div",{children:[s.jsx("img",{src:"/assets/blogs/homer-deploying-friday.png",alt:"homer deploying on a Friday programmer meme",className:"max-w-md rounded-lg"}),s.jsxs("p",{className:"text-sm text-muted-foreground text-center mt-2",children:["source:"," ",s.jsx("a",{href:"https://jivimberg.io/blog/2021/01/22/about-deploying-on-fridays/",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Coding Forest"})]})]})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("a",{href:"https://twitter.com/allenholub/status/1640059900528328704",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Allen Holub"})," ","stands firmly on the other side of the argument."]}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/allen-holub-tweet-1.png",alt:"Allen Holub tweet screenshot 1",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Allen goes on to say that your code quality"," ",s.jsx("a",{href:"https://twitter.com/allenholub/status/1189584085338882048",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"must be inadequate if you refuse to release on Fridays"}),". While I generally hold Allen's opinions in high regard, the statement is quite bold."]}),s.jsx("div",{className:"flex justify-center my-8",children:s.jsx("img",{src:"/assets/blogs/allen-holub-tweet-2.png",alt:"Allen Holub tweet screenshot 2",className:"max-w-lg rounded-lg"})}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Being completely averse to Friday releases can come across as dogmatic. Yet, I disagree with"," ",s.jsx("a",{href:"https://twitter.com/allenholub/status/1189584085338882048",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"Alan"})," ","on 'no Friday releases' being a sign of inadequate quality."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"I have released on a Friday"}),". Bugs can occur any time or weekday and Fridays are no different. It's fundamentally about setting expectations with your team. If there's an opportunity, however small, to reduce the likelihood of a weekend rollback or hotfix, I'm taking those odds any time. Because I value my own and my fellow engineers' weekends."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Sure, bugs ",s.jsx("strong",{className:"text-octo-purple-light",children:"should ABSOLUTELY"})," be found before ever reaching production, but that's not a 100%. I don't see the issue with pushing a bigger release to Monday, if there's no business need to get it out on Friday."]}),s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]}),wR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"navigating the typescript gymnastics #2"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8",children:"Take #2 on popular developer dogma and an attempt at reconciliation between TypeScript lovers and haters."}),s.jsx("hr",{className:"border-border mb-8"}),s.jsxs("figure",{className:"mb-8",children:[s.jsx("img",{src:"/assets/blogs/654b87eb5e250bee25343564_gym-octo-1200.png",alt:"octopus Chad doing some TypeScript gymnastics",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-center text-sm text-muted-foreground mt-2",children:"octopus Chad doing some TypeScript gymnastics"})]}),s.jsxs("div",{className:"prose prose-invert max-w-none",children:[s.jsxs("p",{className:"text-foreground/90 mb-6",children:["I'm firmly in the TypeScript camp. I believe it enhances code readability, maintainability, and reduces bugs and for full disclosure, I've written only about 0.1% of vanilla JavaScript versus TypeScript. Yet,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"I DON'T"})," assume the authority to dictate how someone should run their open-source project or approach their software development."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"However, using typescript is also not an absolute, and some of the arguments brought up against it have some merit."}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-foreground/80",children:[s.jsxs("p",{className:"mb-4",children:['"TypeScript just gets in the way of that for me. Not just because it requires an explicit compile step, but because it pollutes the code with type gymnastics that add ever so little joy to my development experience, and quite frequently considerable grief. Things that should be easy become hard, and things that are hard become ',s.jsx("strong",{className:"text-octo-purple-light",children:"`any`"}),'."']}),s.jsxs("p",{className:"text-sm not-italic",children:["~ DHH in"," ",s.jsx("a",{href:"https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"Turbo 8 is dropping typescript"})]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"I agree, sometimes, types are super hard. But going TypeScript doesn't mean going all in. It's absolutely okay to introduce it iteratively, just like we all strive to develop software in an agile fashion. I would argue it will still help in making the code more readable."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["For example, let's take the popular"," ",s.jsx("a",{href:"https://tanstack.com/query/latest",className:"text-primary hover:underline",target:"_blank",rel:"noopener noreferrer",children:"tanstack-query"})," ","ts library code (shoutout to them, a pleasure to work with!) for constructing a query on the client side to fetch some server data:"]}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://github.com/TanStack/query/blob/main/packages/react-query/src/useQuery.ts",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/dogma-snippet-3.png",alt:"code snippet from tanstack query TS library",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsx("p",{className:"text-foreground/90 mb-6",children:'Certainly some "gymnastics" there with a 4-time generic and triple overload. I think even the most staunch TS supporters would take a second to grok what the octopus is going on here. (the return type of the function basically depends on the type of options you pass in).'}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"It's obviously great to have the types being so exact and generic, but I would argue that even a more basic version of the types already provides value to your users. Especially what options to pass is already greatly helpful (there's a lot)."}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/dogma-snippet-4.png",alt:"a code snippet",className:"w-full rounded-lg"})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Now, it's likely there will be some cursing and type-casting on the user's side, but this is still better than no typing. It's completely fine to use ",s.jsx("code",{children:"`any`"})," and you don't need to do the 'full thing' if you are in a rush. Even the TypeScript docs itself suggest:"]}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-foreground/80",children:[s.jsx("p",{className:"mb-4",children:'"The `any` type is useful when you don\'t want to write out a long type just to convince TypeScript that a particular line of code is okay."'}),s.jsxs("p",{className:"text-sm",children:["~"," ",s.jsx("a",{href:"https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"TypeScript Handbook"})]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Though, if you're always in fire-fighting mode and never have time to make the types work, there's probably deeper issues with your project's operational tempo, but I digress."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Do what works for you and the particular situation. I think it's always worth investing those extra 30 minutes to figure out the types to make the life easier for the next developer looking at the code. But it's all a spectrum and you can approach it iteratively just like everything else. Ultimately, the goal is delivering value ",s.jsx("strong",{className:"text-octo-purple-light",children:"sustainably"})," and"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"maintainably"}),"."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Something we often overlook in engineering: Software has a purpose, right? It's about delivering value to your user and customer. If you can pull that off without static typing, good on ya. Personally, I wouldn't enjoy working on that project, but hey, to each their own."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"I can always... not. 🙂"})]}),s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),jR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"deconstructing popular developer dogmas #1"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8",children:"Opinions in software engineering have gotten more extreme lately. I'll use some notorious examples to show that nuance is an essential engineering principle."}),s.jsx("hr",{className:"border-border mb-8"}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/656f2d9457d5c3ccb9f8aa2b_preacher-octo-1200.jpg",alt:"preacher octopus",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert max-w-none",children:[s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-foreground/80",children:[s.jsx("p",{className:"mb-2 font-bold text-octo-purple-light",children:"dogmatism"}),s.jsx("p",{className:"mb-2 italic text-sm",children:"noun, dog·​ma·​tism ˈdȯg-mə-ˌti-zəm"}),s.jsx("p",{className:"mb-2",children:"1: the expression of an opinion or belief as if it were a fact: positiveness in assertion of opinion especially when unwarranted or arrogant"}),s.jsx("p",{className:"mb-2",children:"2: a viewpoint or system of ideas based on insufficiently examined premises"}),s.jsxs("p",{className:"text-sm not-italic",children:["~"," ",s.jsx("a",{href:"https://www.merriam-webster.com/dictionary/dogmatism",target:"_blank",className:"underline",children:"Merriam Webster"})]})]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Just like everywhere in today's society, it seems that opinions and debates in software engineering have gotten more extreme and absolute lately. The adoption of the term"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"'opinionated'"})," into marketing lingo when describing dev tools is telling."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Take the example of the frankly ridiculous but hilarious fight between DHH's"," ",s.jsx("a",{href:"https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01",target:"_blank",className:"underline",children:"'hate' for typescript"})," ","and the"," ",s.jsx("a",{href:"https://github.com/hotwired/turbo/pull/971",target:"_blank",className:"underline",children:"community's"})," ","backlash. Or the neverending row between unit test absolutists vs. unit test deniers. The singleton shamers! While I think everyone here has a point, I also think that nuances in software engineering often get overlooked."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["I want to share some examples from my own experience to refocus our"," ",s.jsx("a",{href:"https://en.wikipedia.org/wiki/Overton_window",target:"_blank",className:"underline",children:"overton window"})," ","a little. Let's go back to building the right solutions for the right problems instead of bashing our heads over what design pattern is the absolute truth."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Let's start with a popular one."}),s.jsx("h2",{className:"text-2xl font-bold mt-10 mb-6",children:"Singletons are always 100% bad"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["To say singleton patterns in software development are controversial is a bit of an understatement. Diving into the topic you'll learn that singletons suck and they are"," ",s.jsx("a",{href:"https://maximilianocontieri.com/singleton-the-root-of-all-evil",target:"_blank",className:"underline",children:"the root of all evil"}),". But are they?"]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Consider the following controllers in a node.js typescript application:"}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/dogma-snippet-1.png",alt:"code snippet showing dependency injection",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"This clearly looks like dependency injection, often touted as 'best practice'. However, 90% of us would agree that there's not much point to using it here. The code would be equally good and comprehensible if we used a singleton logger."}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/dogma-snippet-2.png",alt:"code snippet showing singleton pattern",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Naturally, this changes if for some reason the logging was actually different for each controller, maybe they are deployed in different environments or the like. But in my 8 years of developing software I haven't come across such a case. And you can always change it once you need it."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Singletons are obviously not a pattern in `current_year` anymore. My point isn't that you should replace your nice dependency injection with singletons, but that there"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"IS"})," nuance to anything in software."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Granted, ",s.jsx("em",{className:"text-octo-teal",children:"logging"})," might be the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"only"})," valid use case for using the singleton pattern, but that's maybe worth a blog post on its own ;)"]}),s.jsxs("figure",{className:"mb-8",children:[s.jsx("img",{src:"/assets/blogs/dogma-snippet-5.png",alt:"code snippet from StackOverflow",className:"w-full rounded-lg"}),s.jsx("figcaption",{className:"text-center text-sm text-muted-foreground mt-2",children:s.jsx("a",{href:"https://stackoverflow.com/questions/228164/on-design-patterns-when-should-i-use-the-singleton/228380#228380",target:"_blank",className:"underline",children:"source: StackOverflow"})})]}),s.jsx("h2",{className:"text-2xl font-bold mt-10 mb-6",children:"Don't be dogmatic"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"When discussing coding best practices, we often forget that they are just that — best practices, not absolutes. There are many variables and business requirements that influence how a particular piece of code or software should be constructed. We shouldn't dish out absolutes like 'singletons are always 100% bad' or 'typescript is making the code worse to read and write'. I'll address more of these dogmas in upcoming articles. I'd appreciate your nuanced take on them."})]}),s.jsx(Ee,{name:"Daniel Draper",title:"lead engineer at Octomind",imageSrc:"/assets/blogs/daniel-draper-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),NR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-6",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:"on type safety in LangChain TS"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8",children:"The unpredictable and non-deterministic nature of the LLM output makes ensuring type safety challenging"}),s.jsx("hr",{className:"border-border mb-8"}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/langchain-llms-nutshell.png",alt:"LLMs in a nutshell",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert max-w-none",children:[s.jsxs("p",{className:"text-foreground/90 mb-6",children:["At Octomind, we are using Large Language Models (LLMs) to interact with web app UIs and extract test case steps that we want to generate. We use the"," ",s.jsx("a",{href:"https://python.langchain.com/docs/get_started/introduction",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"LangChain"})," ","library to build interaction chains with LLMs. The LLM receives a task prompt, and we as developers provide tools the model can utilize to solve the task."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"The unpredictable and non-deterministic nature of the LLM output makes ensuring type safety quite a challenge. LangChain's approach to parsing input and handling errors often leads to unexpected and inconsistent outcomes within the type system. I'd like to share what I learned about parsing and error handling of LangChain."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"I will explain:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-foreground/90 space-y-2",children:[s.jsx("li",{children:"Why did we go for TypeScript in the first place?"}),s.jsx("li",{children:"The issue with LLM output"}),s.jsx("li",{children:"How a type error can go unnoticed"}),s.jsx("li",{children:"What consequences this can have"})]}),s.jsxs("p",{className:"text-muted-foreground text-sm mb-8",children:["*** all code examples are using LangChain TS on the main branch on September 22nd, 2023 (",s.jsx("a",{href:"https://github.com/langchain-ai/langchainjs/tree/0.0.153",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"roughly version 0.0.153"}),")."]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Why LangChain TS instead of Python?"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"There are two languages supported by LangChain - Python and JS/TypeScript."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"There were some pros and some cons with TypeScript:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-foreground/90 space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"On the con side"})," - we have to live with the fact that the TypeScript implementation is somewhat lagging behind the Python version - in code and even more so in documentation. This is a solvable issue, if you are willing to trade the documentation for just going through the source code."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"On the pro side"})," - we don't have to write another service in a different language since we are using TypeScript elsewhere, and we allegedly get guaranteed type safety, of which we are big fans here."]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"We decided to go for the TypeScript version of LangChain to implement parts of our AI-based test discoveries."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Full disclosure, I didn't look into how the Python version handles the issues described below. Have you found similar issues in the python version? Feel free to share them directly in the GitHub issue I created, find the link at the end of the article."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The issue with types in LLMs"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In LangChain, you can provide a set of tools that may be called by the model if it deems it necessary. For our purposes, a tool is simply a class with a ",s.jsx("strong",{className:"text-octo-purple-light",children:"_call"})," ","function that does something that the model can not do on its own, like click on a button on a web page. The arguments to that function are provided by the model."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["When your tool implementation depends on the developer knowing the input format (in contrast to just doing something with text generated by the model), LangChain provides a"," ",s.jsxs("a",{href:"https://github.com/langchain-ai/langchainjs/blob/main/langchain/src/tools/base.ts#L33",className:"underline",target:"_blank",rel:"noopener noreferrer",children:["class called ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool"})]}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["The ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool"})," adds a"," ",s.jsx("a",{href:"https://github.com/colinhacks/zod",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"zod schema"})," ","to the tool, which is used to parse whatever the model decides to call the tool with, so that we can use this knowledge in our code."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:`Let's build our "click" example under the assumption that we want the model to give us a query selector to click on:`}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/4590c10464ac0062a91576ba89752a89#file-snippet-1-ts",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-1.png",alt:"code snippet 1",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Now when you look at this class, it seems reasonably simple without a lot of potential for things to go wrong. But how does the model actually know what schema to supply? It has no intrinsic functionality for this, it just generates a string response to a prompt."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"When LangChain informs the model about the tools at its disposal, it will generate format instructions for each tool. These instructions define what JSON is, and what the specific input schema the model should generate to use a tool."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"For this, LangChain will generate an addition to your own prompt that looks something like this:"}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-foreground/80 text-sm",children:[s.jsx("p",{className:"mb-2",children:"You have access to the following tools."}),s.jsx("p",{className:"mb-2",children:'You must format your inputs to these tools to match their "JSON schema" definitions below.'}),s.jsx("p",{className:"mb-2",children:'"JSON Schema" is a declarative language that allows you to annotate and validate JSON documents.'}),s.jsxs("p",{className:"mb-2",children:['For example, the example "JSON Schema" instance'," ",'{"properties": {"foo": {"description": "a list of test words", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}}']}),s.jsx("p",{className:"mb-2",children:'would match an object with one required property, "foo". The "type" property specifies "foo" must be an "array", and the "description" property semantically describes it as "a list of test words". The items within "foo" must be strings.'}),s.jsxs("p",{className:"mb-2",children:["Thus, the object ",'{"foo": ["bar", "baz"]}',' is a well-formatted instance of this example "JSON Schema". The object ','{"properties": {"foo": ["bar", "baz"]}}'," is not well-formatted."]}),s.jsx("p",{className:"mb-2",children:"Here are the JSON Schema instances for the tools you have access to:"}),s.jsxs("p",{children:["click: left click on an element on a web page represented by a query selector, args:"," ",'{"selector":{"type":"string","description":"The query selector to click on."}}']})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Don't trust the LLM"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Now we have a best-effort way to make the model call our tool with inputs in the correct schema. Best effort unfortunately does not guarantee anything. It is entirely possible, that the model generates input that does ",s.jsx("em",{className:"text-[#15D7AB]",children:"not"})," adhere to the schema."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["So let's take a look at the implementation of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool"})," to see how it deals with that issue."," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool.call"})," is the function that eventually calls our ",s.jsx("strong",{className:"text-octo-purple-light",children:"_call"})," method from above."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"It starts like this:"}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/2674f361eafa2a9741fc1b65e8ed7ae9",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-2.png",alt:"code snippet 2",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["The signature of ",s.jsx("strong",{className:"text-octo-purple-light",children:"arg"})," is interpreted as follows:"]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["If after parsing the tool's schema, the output can be just a string, this can also be a string, or whatever object the schema defines as input. This is the case if you define your schema as"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"schema = z.string()"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In our case, our schema can not be parsed to a string, so this simplifies to the type"," ",s.jsxs("strong",{className:"text-octo-purple-light",children:["{ selector: string }",", or ClickSchema"]}),"."]}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-4",children:"But is this actually the case?"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["According to the implementation, we only check that the input actually adheres to the schema inside of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"call"}),". The signature reads like we have already made some assumptions about the input."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"So one might replace the signature with something like:"}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/bded78f8223cb2b123e637ef087813b7",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-3.png",alt:"code snippet 3",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"But looking at it further, even this has issues. The only thing we know for certain is that the model will give us a string. This means there are two options:"}),s.jsxs("p",{className:"text-foreground/90 mb-4",children:["1. ",s.jsx("strong",{className:"text-octo-purple-light",children:"call"})," really should have the following signature:"]}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/e445da1274fad29b6502bed8b9ff8622",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-4.png",alt:"code snippet 4",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsx("p",{className:"text-foreground/90 mb-4",children:"2. There is another element to this"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-foreground/90 space-y-2",children:[s.jsx("li",{children:"Something must have already decided that the string returned by the model is valid JSON and have parsed it."}),s.jsxs("li",{children:["In case that ",s.jsx("strong",{className:"text-octo-purple-light",children:"z.output extends string"}),", something somewhere must have already decided that string is an acceptable input format for the tool, and we do not need to parse JSON. (A string by itself is not valid JSON,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:'JSON.parse("foo")'})," will result in a SyntaxError)."]})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"Introducing the OutputParser class"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Of course, the second option is what is happening. For this use case, LangChain provides a concept called"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"OutputParser"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Let's take a look at the default one (",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredChatOuputParser"}),") and its"," ",s.jsxs("a",{href:"https://github.com/langchain-ai/langchainjs/blob/main/langchain/src/agents/structured_chat/outputParser.ts#L112",className:"underline",target:"_blank",rel:"noopener noreferrer",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"parse"})," method"]})," ","in particular."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"We don't need to understand every detail, but we can see that this is where the string that the model produces is parsed to JSON, and errors are thrown if it is not valid JSON."}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/74d492d872c5f66773930900abde9f2e",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-5.png",alt:"code snippet 5",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["So, from this we either get ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction"})," or"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentFinish"}),". We don't need to concern ourselves with"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentFinish"}),", since it is just a special case to indicate that the interaction with the model is done."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:[s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction"})," is defined as:"]}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/545e5051a650c1cc4d888cef917914f4",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-6.png",alt:"code snippet 6",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["By now you might have already seen - neither"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction"})," nor the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredChatOutputParserWithRetries"})," is generic, and there is no way to connect the type of ",s.jsx("strong",{className:"text-octo-purple-light",children:"toolInput"})," with our ",s.jsx("strong",{className:"text-octo-purple-light",children:"ClickSchema"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Since we don't know which tool the agent has actually selected, we can not (easily) use generics to represent the actual type, so this is expected. But worse,"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"toolInput"})," is typed as"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"string"}),", even though we just used"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"JSON.parse"})," to get it!"]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Consider the positive case where the model produced output that matches our schema, let's say the string"," ",s.jsx("strong",{className:"text-octo-purple-light",children:'"{"selector": "myCoolButton"}"'})," (wrapped in all the extra fluff LangChain requires to correctly parse). Using"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"JSON.parse"}),", this will deserialize to an object"," ",s.jsx("strong",{className:"text-octo-purple-light",children:'{ selector: "myCoolButton" }'})," and"," ",s.jsx("em",{className:"text-[#15D7AB]",children:"not"})," a ",s.jsx("strong",{className:"text-octo-purple-light",children:"string"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["But because ",s.jsx("strong",{className:"text-octo-purple-light",children:"JSON.parse"}),"'s return type is"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"any"}),", the typescript compiler has no chance of realizing this. Unfortunately for us, this also means that we, as developers, have a hard time to realize this."]}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The impact on our production code"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["To understand why this is troublesome, we need to look into the execution loop where the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction"}),"s are used to actually invoke the tool."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["This happens"," ",s.jsx("a",{href:"https://github.com/langchain-ai/langchainjs/blob/main/langchain/src/agents/executor.ts#L131",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"here"})," ","in ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentExecutor._call"}),". We don't really need to understand everything that this class does. Think of it as the wrapper that handles the interaction of the model with the tool implementations to actually call them."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["The ",s.jsx("strong",{className:"text-octo-purple-light",children:"_call"})," method is quite long, so here is a reduced version that only contains parts relevant for our problem (these methods are simplified parts of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"_call"})," and not in the actual code base of LangChain)."]}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/1e2e2e80f5885ad439a6ae76c2b3e1c5",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-7.png",alt:"code snippet 7",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["The first thing that happens in the loop is to look for the next action to execute. This is where the parsing using the ",s.jsx("strong",{className:"text-octo-purple-light",children:"OutputParser"})," comes in, and where its exceptions are handled."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["You can see that in the case of an error, the"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"toolInput"})," field will always be a string (if"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"this.handleParsingErrors"})," is a function, the return type is also ",s.jsx("strong",{className:"text-octo-purple-light",children:"string"}),")."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["But we have just seen above, that in the non-error case"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"toolInput"})," will be parsed JSON! This is inconsistent behavior, we never parse the output of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"handleParsingErrors"})," to JSON."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Let's look at how the loop continues. The next step is to call the selected tool with the given input:"}),s.jsx("figure",{className:"mb-8",children:s.jsx("a",{href:"https://gist.github.com/ma-zah/97d2ce8a94b05528d9e7dccac07fb3db",target:"_blank",rel:"noopener noreferrer",children:s.jsx("img",{src:"/assets/blogs/langchain-snippet-8.png",alt:"code snippet 8",className:"w-full rounded-lg hover:opacity-90 transition-opacity"})})}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["We only pass the previously computed output on to the tool in"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"tool.call(action.toolInput)"}),"!"]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"And in case this causes another error, we re-use the same function to handle parsing errors that will return a string that is supposed to be the tool output in the error case."}),s.jsx("h3",{className:"text-xl font-bold text-white mt-8 mb-4",children:"Let's summarize all the issues:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-foreground/90 space-y-2",children:[s.jsx("li",{children:"We parse the model's output to JSON and use that parsed result to call a tool"}),s.jsx("li",{children:"If the parsing succeeds, we call the tool with any valid JSON"}),s.jsx("li",{children:"If the parsing fails, we call the tool with a string"}),s.jsxs("li",{children:["The tool parses the input with zod, which will only work in the error case if the schema is just a"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"const stringSchema = z.string()"})]}),s.jsxs("li",{children:["We have not covered this, but using"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"const stringSchema = z.string()"})," as the tool schema will not type check at all, since the generic argument of"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool"})," is"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"T extends z.ZodObject"}),", and"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"typeof stringSchema"})," does not fulfil that constraint"]}),s.jsxs("li",{children:["The signature of ",s.jsx("strong",{className:"text-octo-purple-light",children:"tool.call"})," allows this to type check, since we don't know specifically which tool we have at the moment, so string and any json is potentially valid"]}),s.jsx("li",{children:"The actual type check for this happens at runtime inside this function"}),s.jsxs("li",{children:["The developer implementing the tool has no idea about this. Since only"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool._call"})," is abstract, you will always get what the schema indicates, but"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool.call"})," will fail, even if you have supplied a function ",s.jsx("strong",{className:"text-octo-purple-light",children:"handleParsingErrors"}),"."]}),s.jsxs("li",{children:["Whatever the tool gets called with is serialized into"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction.toolInput: string"}),", which is not correctly typed"]}),s.jsxs("li",{children:["The library user has access to the ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentStep"}),"s with wrongly typed ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentAction"}),"s, since it is possible to request them as a return value of the overall loop using"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"returnIntermediateSteps=true"}),"."]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Whatever the developer does now is definitely not type safe!"}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"How did we run into this problem?"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["At Octomind, we are using the ",s.jsx("strong",{className:"text-octo-purple-light",children:"AgentStep"}),"s to extract the test case steps that we want to generate. We noticed that the model often makes the same errors with the tool input format."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["Recall our ",s.jsx("strong",{className:"text-octo-purple-light",children:"ClickSchema"}),", which is just"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"{ selector: string }"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In our clicking example it would either generate according to the schema, or"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"{ element: string }"}),", or just a string which is the value we want, like ",s.jsx("strong",{className:"text-octo-purple-light",children:'"myCoolButton"'}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["So we built an auto-fixer for these common error cases. The fixer basically just checks whether it can fix the input using either of the options above. The earliest we can inject this code without overwriting a lot of the planning logic that LangChain provides is in"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool.call"}),"."]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["We can not handle it using ",s.jsx("strong",{className:"text-octo-purple-light",children:"handleParsingErrors"}),", since that receives only the error as input, and not the original input. Once you are overwriting"," ",s.jsx("strong",{className:"text-octo-purple-light",children:"StructuredTool.call"}),", you are relying on the signature of that function to be correct, which we just saw is not the case."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"At this point, I was stuck having to figure out all of the above to see why I am getting wrongly typed inputs."}),s.jsx("h2",{className:"text-2xl font-bold text-white mt-12 mb-4",children:"The solution to type safety"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"While these hurdles can be frustrating, they also present opportunities to take a deep dive into the library and come up with possible solutions instead of complaining."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"I have opened two issues at LangChain JS/TS to discuss ideas on how to solve these problems:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 text-foreground/90 space-y-2",children:[s.jsxs("li",{children:["Issue 1 -"," ",s.jsx("a",{href:"https://github.com/langchain-ai/langchainjs/issues/2710",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"https://github.com/langchain-ai/langchainjs/issues/2710"})]}),s.jsxs("li",{children:["Issue 2 -"," ",s.jsx("a",{href:"https://github.com/langchain-ai/langchainjs/issues/2711",className:"underline",target:"_blank",rel:"noopener noreferrer",children:"https://github.com/langchain-ai/langchainjs/issues/2711"})]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Feel free to jump in!"})]}),s.jsx(Ee,{name:"Veith Röthlingshöfer",title:"ML engineer at Octomind",imageSrc:"/assets/blogs/veith-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]}),kR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-16",children:s.jsxs("article",{className:"max-w-3xl mx-auto px-6",children:[s.jsx(de,{}),s.jsxs("h1",{className:"text-4xl md:text-5xl font-bold text-octo-teal mb-6",children:["Testing Pyramid:",s.jsx("br",{}),"An Evolutionary Tale"]}),s.jsxs("p",{className:"text-xl text-muted-foreground mb-8",children:["Change is ubiquitous in software development. New languages, tools and frameworks are being constantly invented and old ones toppled. Yet the one thing unwilling to move is the ",s.jsx("em",{children:"Testing Pyramid"}),". It seems to survive technological erosion and the attacks of armies of unit test haters. Let's explore its origins, impact, and how breakthroughs in testing reimagine the pyramid for the modern testing era."]}),s.jsx("hr",{className:"border-border mb-8"}),s.jsx("figure",{className:"mb-8",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-3pyramids.png",alt:"3 multilayered pyramids conceptual",className:"w-full rounded-lg"})}),s.jsxs("div",{className:"prose prose-invert max-w-none",children:[s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"The inception of the testing pyramid"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Flashback to 2009: At the time, testing consisted mainly of two types of tests: Unit and UI. The latter is often also referred to as end-to-end (E2E) test. However, Mike Cohn, in his blog article and his book 'Succeeding with Agile' argues that something is missing:"}),s.jsxs("blockquote",{className:"border-l-4 border-white pl-6 my-8 text-foreground/80",children:[s.jsxs("p",{className:"mb-2 italic",children:["'Testing [through UI] would ",s.jsx("strong",{children:"certainly work"})," but would be ",s.jsx("strong",{children:"brittle"}),","," ",s.jsx("strong",{children:"expensive"}),", and ",s.jsx("strong",{children:"time-consuming."}),"'"]}),s.jsxs("p",{className:"text-sm not-italic",children:["‍",s.jsx("a",{href:"https://www.mountaingoatsoftware.com/blog/the-forgotten-layer-of-the-test-automation-pyramid",target:"_blank",className:"underline text-octo-purple-light",children:"Mike Cohn (2009)"})]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"He postulated that not all tests need UI, so his 2009 solution was the service test (later called integration test). It's a new layer that tests the services separately from the UI."}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-mike-cohn.png",alt:"Mike Cohn added a new service layer to the testing pyramid",className:"mx-auto rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"Mike Cohn pioneered the service layer"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Based on this insight, Mike Cohn introduced the Testing Pyramid in his book 'Succeeding with Agile.' The pyramid emphasized a hierarchy of tests based on their granularity, speed, and costs. At the base stood unit tests, which were numerous but quick and cheap. The service/integration tests that balance granularity and scope were in the middle. At the top were the UI / e2e tests: fewer, slower, and costlier but broader in scope, thus giving you more confidence."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In their"," ",s.jsx("a",{href:"https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html",target:"_blank",className:"underline",children:"2015 blog post,"})," ","Google agreed with Mike Cohn and outed themselves as integration test fans. They were not alone. Many advocates followed:"]}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-guillermo-tweet.png",alt:"guillermo rauch tweet on integration testing",className:"mx-auto rounded-lg max-w-md w-full"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"for Vercel founder and JS legend Guillermo integration tests should be in the majority"}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-swyx-tweet.png",alt:"Swyx tweet on integration testing",className:"mx-auto rounded-lg max-w-md w-full"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"and swyx agrees"}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In the same blog",s.jsx("a",{href:"https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html",target:"_blank",className:"underline",children:","})," ","Google suggested an ideal distribution of 70% unit tests, 20% integration tests, and 10% e2e tests. This allocation was not a strict rule but a guideline reflecting each testing level's confidence (leftmost arrow in the image below), cost and speed (right side)."]}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-confidence-chart.png",alt:"effect on confidence, costs and speed of specific test layers and distribution",className:"mx-auto rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"effect on confidence, costs and speed (from left to right) of specific test layers and distribution"}),s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"Questioning the pyramid - alternate shapes emerge"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"As software development practices evolved and tools improved, the rigidity of the pyramid began to erode. It gave birth to a slew of alternative shapes:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 space-y-4 text-foreground/90",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Test Diamond"}),s.jsx("br",{}),"Emphasizes integration and system tests, squeezing unit and E2E tests to the corners, arguing that integration tests offer a more cost-effective way to catch bugs."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Test Ice Cream Cone"}),s.jsx("br",{}),"A satirical take that suggests that many organizations put more emphasis on UI and manual tests (the top), with a decreasing focus on integration and unit tests, leading to a fragile testing base. In 2015,"," ",s.jsx("a",{href:"https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html",target:"_blank",className:"underline",children:"Google"})," ","called it an anti-pattern."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Test Crab"}),s.jsx("br",{}),"Emphasizes wide-ranging UI tests and fewer unit, component, and API tests (making up the crab's legs). This reflects scenarios where user interfaces have complex interactions."]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Test Trophy"}),s.jsx("br",{}),"Advocates a broad base of static tests to complement slightly fewer unit tests."]})]}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-shapes.png",alt:"software testing shapes - pyramid, ice cream cone, crab and trophy",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"four (un) popular testing shape alternatives, source: @leichteckig"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"The testing world is in the midst of a paradigm shift. The journey has been enlightening, from the early days of relying heavily on unit tests to today's emphasis on end-to-end (e2e) tests. Let's walk through this evolutionary tale and understand each testing layer's significance, strengths, and challenges."}),s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"A sturdy foundation: unit tests"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Unit tests serve as the bedrock of the testing pyramid. Their benefits include:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 space-y-2 text-foreground/90",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Precision"})," - unit tests can identify issues in specific code segments"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Speed"})," - they're quick to write and even quicker to run"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Promotes good practices"})," - encourages modular, decoupled code and is foundational for the widely-accepted Test Driven Development (TDD) approach"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Documentation"})," - they effectively serve as documentation, making it easier for developers to understand the purpose and behavior of code segments"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Regression detection"})," - spotting regressions early in the development cycle leads to considerable savings in terms of time and effort."]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Yet, it's not all sunshine and rainbows. Unit tests can be tightly coupled – a minor code change can result in many failing tests. This often provides developers with a false sense of security, leading to potential oversights. And makes maintaining them very annoying."}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-unit-tests.png",alt:"unit test example",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"example of good practice for unit tests"}),s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"The under- and over-appreciated prodigy: integration tests"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Integration tests can be seen as the middle child of the testing family – pivotal yet often under- or overvalued depending on the organization you work for. They ensure that various parts of your application, such as controllers, databases (both in-memory and dockerized), and UI components, work harmoniously."}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-integration-tests.png",alt:"integration tests diagram",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"integration tests can be used to test many things"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"The reason why integration tests have earned their place in the testing pyramid is:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 space-y-2 text-foreground/90",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Higher confidence"})," - they offer more confidence in the functionality than unit tests"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Contract adherence"}),"- they're instrumental in ensuring that APIs function as detailed in their documentation, like Swagger docs"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Cost-Efficiency"}),"- they balance the detail of unit tests and the breadth of E2E tests, offering a favorable cost-benefit ratio"]})]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"However, the challenges include complicated setups involving test databases or external services, slower execution times due to reliance on external components, and greater flakiness due to the nature of the more complex execution environment (think HTTP timeouts, etc)."}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-cable-chaos.png",alt:"cable chaos conceptual",className:"w-full rounded-lg"})}),s.jsx("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:"integration tests often require complicated setup (test db, external services, etc.)"}),s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"The rise of e2e testing"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"End-to-end tests simulate user behaviors and interactions, covering the gamut from frontend to backend to databases. But they haven't always been the darling of the testing community."}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"To what end? We consider e2e tests to start at the browser all the way to the backend. Historically, they faced criticism for:"}),s.jsxs("ul",{className:"list-disc pl-6 mb-6 space-y-2 text-foreground/90",children:[s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Lack of precision"}),"- difficulties in pinpointing specific issues"]}),s.jsxs("li",{children:[s.jsx("strong",{className:"text-octo-purple-light",children:"Maintenance woes"})," - a single code change often leads to multiple e2e test failures"]}),s.jsxs("li",{children:["‍",s.jsx("strong",{className:"text-octo-purple-light",children:"Flakiness"}),"- numerous uncontrollable variables made them unreliable"]})]}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["However, the tide has started to turn, especially with the advent of modern e2e testing tools like the"," ",s.jsx("a",{href:"https://playwright.dev/",target:"_blank",className:"underline",children:"Playwright testing framework"}),". Open-sourced by Microsoft, Playwright, akin to Cypress, offers innovative features such as live replay through traces and an intuitive API design, resembling a modern Selenium. The testing arena is witnessing a shift towards testing user behavior, emphasizing e2e testing's increasing importance."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"And the end-to-end camp is getting louder. Some go as far as proposing an upside down arrangement of the Testing Pyramid. But I wouldn't go that far. A solid base of unit tests has its rationale. It gives you the quick feedback and pinpointing needed for fast iterations."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["The ",s.jsx("strong",{className:"text-octo-purple-light",children:"Testing Hourglass"})," is a promising way out of the testing shape agony. Once described as an anti-pattern, thanks to the progress in end-to-end (especially UI) testing, some integration-level checks can be executed from the top. We use it ourselves, with promising results. We will continue to blog about our findings."]}),s.jsx("figure",{className:"mb-4",children:s.jsx("img",{src:"/assets/blogs/testing-pyramid-hourglass.jpg",alt:"heavy on e2e and unit tests, light on integration testing - the hourglass test distribution",className:"mx-auto rounded-lg"})}),s.jsxs("p",{className:"text-sm text-muted-foreground mb-8 text-center",children:["hourglass test distribution, image by"," ",s.jsx("a",{href:"https://1.bp.blogspot.com/-e6bsyqU1yt8/X48dcQU9uLI/AAAAAAAAAbM/Ct-p0T6Y728g_gyHYZ_DWT6Ks2MPhrgfwCLcBGAsYHQ/s320/Copy%2Bof%2BGoogle%2BTesting%2BBlog_%2BFixing%2Ba%2BTest%2BHourglass%2B%25281%2529%2B-%2BEdited.jpg",target:"_blank",className:"underline",children:"Alan Myrvold"})]}),s.jsx("h2",{className:"text-2xl font-bold text-white mb-6",children:"Only testing gives you the necessary confidence"}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"While the classic testing pyramid might seem slightly antiquated, given the advancements in e2e tests, it's essential to avoid getting trapped in dogmatic thinking. Use the right mix of unit, integration, and e2e tests depending on your project's nature, the tools at your disposal, and the risks you're willing to accept."}),s.jsxs("p",{className:"text-foreground/90 mb-6",children:["In any scenario, one golden rule remains steadfast -"," ",s.jsx("strong",{className:"text-octo-purple-light italic",children:"always write tests and design them to benefit the team"}),". The goal is to deliver quality code with confidence, irrespective of the testing mix."]}),s.jsx("p",{className:"text-foreground/90 mb-6",children:"Also, since the cost-benefit analysis for e2e testing is changing significantly, stay informed about the latest in e2e testing. Combine different testing forms intelligently, and be discerning about what and how you test – after all, test coverage is a tool, not a target."})]}),s.jsx(Ee,{name:"Marc Mengler",title:"Co-Founder & CEO at octomind",imageSrc:"/assets/blogs/marc-mengler-author.webp"}),s.jsx(ge,{})]})}),s.jsx(se,{})]});function _R({open:e,onOpenChange:t}){return s.jsx(s.Fragment,{children:s.jsx(uk,{open:e,onOpenChange:t,children:s.jsx(Pf,{variant:"white",className:"sm:max-w-lg min-h-[530px] max-h-[100dvh]",children:s.jsx("div",{className:"hs-form-frame","data-region":"eu1","data-form-id":"7263ad7e-8f75-4431-be59-f8921932ad8f","data-portal-id":"139584260"})})})})}function CR(){const[e,t]=f.useState(!1);return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsxs("header",{className:"mb-10",children:[s.jsx("h1",{className:"animate-fade-in-up text-4xl md:text-5xl font-bold mb-4 text-octo-teal",children:"QA Agent in Your CI/CD Pipeline"}),s.jsx("p",{className:"animate-fade-in-up text-xl text-muted-foreground",children:"Your CI/CD pipeline runs tests you wrote last quarter. It has no idea what was Vibe Coded today."})]}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/qa-agent-cicd-pipeline.png",alt:"QA agent in a CI/CD pipeline",className:"w-full rounded-lg mb-12",loading:"lazy"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The Problem Nobody Wants to Admit"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Your test suite doesn't think. It executes what you told it to check six months ago when the codebase looked different."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Someone opens a PR adding payment retry logic. Your tests pass because they check the happy path. They don't know about the new edge cases that just got introduced."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"A human needs to look at that diff, understand what changed, figure out what could break, and write new tests. That takes hours. The PR sits waiting. Your velocity promise evaporates while someone manually thinks through edge cases."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This gets worse with AI-generated code. You vibe-code a feature in 20 minutes. Your QA team spends three days mapping what could break."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Old Way vs New Way"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The old way: hire more QA engineers to write test plans faster. Throw people at a capacity problem."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The new way: put an autonomous agent in your CI/CD pipeline that actually thinks about what needs testing."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Not another testing framework. Not a coding agent that dumps out shallow Playwright tests."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"An agent that watches your PRs, analyzes what changed, decides what matters, and generates real coverage before humans even start code review."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What This Actually Looks Like"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We integrated this last month. A developer opened a PR with new currency support in the checkout flow."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The agent triggered automatically. It analyzed the diff and spotted 3 edge cases we missed — currency conversion failures, timeout handling, edge cases in the error states."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It clicked through our test environment to verify the changes worked. Then it generated a 90-second video showing the payment flow in action."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The video appeared in PR comments before our first human review."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Our tech lead watched it and immediately spotted a UX issue with the loading state. The spinner disappeared too early on slow connections. Fixed before merge."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"By the time we approved the PR, the agent had generated 6 end-to-end test cases ready to pull. We went from hours of manual test planning to minutes of automated analysis."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The Shift That Matters"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Your QA engineers stop being test plan writers. They become test plan reviewers and edge case hunters."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The agent handles happy paths, standard error handling, and basic edge cases. Your team reviews the output and adds the non-obvious stuff—business logic quirks, integration gotchas, user behavior patterns that break assumptions."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Better division of labor. Automate repetitive thinking. Use humans for judgment."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What You Get"}),s.jsxs("ul",{className:"text-octo-text leading-relaxed mb-4 list-none pl-0 space-y-2",children:[s.jsx("li",{children:"→ Test plans generated in minutes, not hours"}),s.jsx("li",{children:"→ Video documentation of how your PR actually works"}),s.jsx("li",{children:"→ Coverage that adapts to every change automatically"}),s.jsx("li",{children:"→ QA team focused on high-value work instead of repetitive planning"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The agent runs on every PR. You can't ignore it. Coverage increases immediately because it's automatic."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The Reality Check"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This doesn't work if your codebase is chaos. The agent needs readable code and clear patterns."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It handles repetitive coverage so they can focus on what automation misses."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Once it's running, the ROI is immediate. Tests get written. Videos get generated. Your team stops drowning in manual work."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Try It"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We're opening a waitlist."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"If you're shipping AI-generated code fast and testing can't keep up, this changes your workflow."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"If you're a CTO watching your QA team drown in manual test planning, this frees them up."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Sign up for the waitlist. We'll help you integrate it into your pipeline."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:"Stop waiting for humans to think through test plans. Let the agent do it."}),s.jsx("div",{className:"not-prose mt-10 flex flex-col sm:flex-row gap-3 items-start",children:s.jsx(ut,{onClick:()=>t(!0),children:"Join the waitlist"})}),s.jsx(_R,{open:e,onOpenChange:t})]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const SR=()=>s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 text-octo-teal animate-fade-in-up",children:"Who's Actually Vibe Coding? The Data Doesn't Match the Hype"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8 animate-fade-in-up",children:'A guy posted a "prompting bible" for Lovable* on LinkedIn. He got 10,000 requests in days. We scraped 500 of those people and enriched the data. I expected broke founders building MVPs for $100. The data told a completely different story.'}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/vibe-coding-data-expectation-reality.png",alt:"Expectation vs Reality - Vibe coding user demographics chart",className:"w-full rounded-lg mb-12"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The Narrative We All Believe"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The vibe coding story goes like this: solo founders who can't afford developers, bootstrappers building MVPs in a weekend. Give a prompt, AI generates an app, test with real users."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It's the democratization of software. Anyone with an idea and $20/month can compete with venture-backed startups."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's what I assumed we'd find when we looked at who actually wanted that guide."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What We Actually Found"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"34% of people requesting that guide work at companies with 5,000+ employees."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Another 13% at 1,000-5,000 employees."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's 47% working at enterprises with over 1,000 people. These aren't garage startups. These are people with engineering teams, IT departments, and software budgets."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Solo founders and tiny startups (0-10 employees)? Only 16%."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The data inverted my assumptions entirely."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Who Are These People?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Here's where it gets interesting. The #1 group isn't engineers. Engineers are only 15%."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The breakdown:"}),s.jsxs("ul",{className:"list-none pl-0 space-y-2 mb-6",children:[s.jsx("li",{className:"text-octo-text",children:"→ Executives and strategists: 22%"}),s.jsx("li",{className:"text-octo-text",children:"→ Design and UX: 16%"}),s.jsx("li",{className:"text-octo-text",children:"→ Engineers: 15%"}),s.jsx("li",{className:"text-octo-text",children:"→ Product managers: 10%"}),s.jsx("li",{className:"text-octo-text",children:"→ People and HR: 9%"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:`These are people who normally wait months for developers to build internal tools. People who submit tickets that never get prioritized. People who've been told "it's on the roadmap" for two years.`}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Now they build it themselves."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What Are They Actually Building?"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Not the next unicorn SaaS. Not consumer apps hitting Product Hunt."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Internal tools. Process automation. Custom dashboards IT said would take six months. Scripts to connect systems that don't talk to each other."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It's shadow IT, but faster."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:`An executive describes "a tool that pulls data from our CRM and formats it for weekly reports" and gets something working in an afternoon. Messy, probably won't scale, but solves the problem right now.`}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's way more disruptive than another todo app."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"Why This Matters"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The vibe coding revolution isn't happening in garages. It's happening inside corporations, driven by people tired of waiting for their engineering teams."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Is this good? Honestly, I don't know."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"It empowers people closest to the problems. The person who knows exactly what they need can now build it without filing a ticket, waiting three months, and getting something that doesn't quite work."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"But it creates maintenance nightmares. Security risks. Undocumented tools that break when models update."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Engineers complain that vibe coding doesn't work on real codebases. They're right. But they're missing something important: they're not the primary users. The primary users don't care about maintainability. They care about solving problems today."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"What Happens Next"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This doesn't reverse. The tools will get better and worse simultaneously."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Better at generating code. Worse at maintainability."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Why worse? Easier generation means more code, more dependencies, more edge cases. And nobody maintains it because nobody fully understands it."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Companies will split into two worlds:"}),s.jsxs("ul",{className:"list-none pl-0 space-y-2 mb-6",children:[s.jsx("li",{className:"text-octo-text",children:"→ Core systems that engineers own, where code quality matters"}),s.jsx("li",{className:"text-octo-text",children:"→ A sprawling ecosystem of internal tools that business users build themselves"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:`IT will fight it. Then they'll give up and try to govern it. We'll see "approved vibe coding platforms" and "citizen developer training programs."`}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"The Real Market"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's the real opportunity here. Not building the next Airbnb with prompts."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Just 10,000 internal tools that nobody wanted to build, but everybody needed yesterday."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The people with the problems now have tools to solve them. Messy tools. Tools that will break. But tools that exist today instead of promises for next quarter."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The data is clear: vibe coding already went mainstream. Just not where anyone expected."})]}),s.jsx("div",{className:"border-t border-octo-purple/20 mt-12 pt-6",children:s.jsxs("p",{className:"text-sm text-muted-foreground",children:["[1]Post:"," ",s.jsx("a",{href:"https://www.linkedin.com/posts/matt-graham-nocode_ive-spent-6-months-mastering-ai-prompt-engineering-activity-7386386401845673985-avgM?utm_source=share&utm_medium=member_desktop&rcm=ACoAABpFqXYBA3ZZdpLVeRllR-C7d4fTVYeXdyY",target:"_blank",rel:"noopener noreferrer",className:"text-octo-teal hover:underline",children:"Prompting bible for Lovable on LinkedIn"})]})}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]});function ER(){return s.jsxs("div",{className:"min-h-screen bg-octo-dark",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsx("main",{className:"relative z-10 pt-32 pb-20",children:s.jsxs("article",{className:"container mx-auto px-4 max-w-4xl",children:[s.jsx(de,{}),s.jsx("h1",{className:"text-4xl md:text-5xl font-bold mb-6 text-octo-teal animate-fade-in-up",children:"openclaw in projects: real automation for people who'd rather be coding"}),s.jsx("p",{className:"text-xl text-muted-foreground mb-8 animate-fade-in-up",children:"OpenClaw is showing up in developer workflows as a self-hosted automation engine. Here's how teams actually use it — from CI/CD monitoring to expense management — and what we learned running it ourselves."}),s.jsxs("div",{className:"animate-fade-in-up",children:[s.jsx("img",{src:"/assets/blogs/openclaw-octoclaw.png",alt:"OctoClaw - Your AI in a Secure Sandbox",className:"w-full rounded-lg mb-12",loading:"lazy"}),s.jsxs("div",{className:"prose prose-lg prose-invert max-w-none",children:[s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Your CI/CD pipeline just failed. You didn't notice for 47 minutes because you were deep in a refactor. By the time you see it, two colleagues have already pushed on top of the broken build."}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["This is not a problem you should be solving with your attention. ",s.jsx("a",{href:"https://github.com/openclaw",className:"text-octo-purple hover:underline",children:"OpenClaw"})," is an open-source, self-hosted AI assistant that connects to your tools — terminal, browser, APIs — and handles the stuff that doesn't require your judgment but currently demands your time."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"common patterns in the wild"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"Before diving into our own setup, two broad categories of OpenClaw usage keep showing up across teams:"}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"devops & code management"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:`Teams point OpenClaw at their CI/CD pipelines to catch failures, pull error logs, and post formatted alerts to chat — no more "did anyone see the build is red?" It also handles git operations like summarizing PRs and running standard workflows via natural language. Some teams let it analyze terminal errors and propose fixes, though most keep it in "suggest, don't apply" mode. Wise.`}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"administrative automation"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"On the non-code side: inbox triage, calendar conflict detection, and — most interestingly — web automation via Puppeteer. OpenClaw can navigate websites, fill out forms, and scrape data from services that never bothered to build an API. You describe what you need in natural language, and it orchestrates the browser session. Fragile? Yes. Better than doing it manually every week? Absolutely."}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The OpenClaw differentiator across both categories is self-hosting. Your data stays on your infrastructure. For teams handling sensitive communications or operating under strict data residency requirements, that matters."}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"how we actually use openclaw at octomind"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The Octomind blog has always been about showing our actual work rather than theorizing, so here's a real workflow I built."}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"The problem: expense management was eating hours"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"My old flow looked like this:"}),s.jsxs("ol",{className:"text-octo-text leading-relaxed mb-4 list-decimal pl-6 space-y-2",children:[s.jsx("li",{children:"Team member posts an expense and a photo of the invoice in Slack"}),s.jsx("li",{children:"The expenses pile up until someone (me) finds time to go through them"}),s.jsx("li",{children:"For each one, I download the invoice, open our bank account, and manually initiate the reimbursement"}),s.jsx("li",{children:"Repeat until done or until I lose the will to continue"})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"This is slow. It's annoying. And it's exactly the kind of task that's too irregular for a proper enterprise tool but too frequent to just ignore."}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"The new flow: OctoClaw watches Slack"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We pointed an OpenClaw instance (we call it OctoClaw internally, because of course we did) at our expenses Slack channel. Here's what it does now:"}),s.jsxs("ol",{className:"text-octo-text leading-relaxed mb-4 list-decimal pl-6 space-y-2",children:[s.jsxs("li",{children:[s.jsx("strong",{children:"Observes"})," the Slack channel for new expense posts"]}),s.jsxs("li",{children:[s.jsx("strong",{children:"Extracts"})," the invoice image and message details"]}),s.jsxs("li",{children:[s.jsx("strong",{children:"Composes"})," a structured email with the invoice attached, the amount, and the recipient's payment details"]}),s.jsxs("li",{children:[s.jsx("strong",{children:"Validates"})," the recipient against an approved list — no random payments going out because someone typo'd a name"]}),s.jsxs("li",{children:[s.jsx("strong",{children:"Sends"})," the prepared email to me for review"]})]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"That's it. I get a clean email with everything I need. I review it, forward it to the bank, done."}),s.jsx("h3",{className:"text-xl font-bold mt-8 mb-3",children:"What's next"}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"The current flow still has me as the final reviewer. That's intentional — I'm building trust incrementally. Once I've run this for long enough to be confident in the validation logic, the plan is to have OctoClaw send the reimbursement email directly to the bank."}),s.jsx("blockquote",{className:"border-l-4 border-octo-purple pl-4 italic text-octo-text my-6",children:`Start with "show me what you'd do," graduate to "do it and tell me," eventually arrive at "just do it."`}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{children:"Incrementally extending trust to an AI agent"})," is, I think, the right pattern for anything touching money."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"the honest caveats"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["OpenClaw has rough edges, and ",s.jsx("a",{href:"https://scalevise.com/resources/openclaw-automations-explained/",className:"text-octo-purple hover:underline",children:"there are a few worth flagging"}),"."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{children:"Scaling limits are real."})," OpenClaw is designed for personal and small-team automation. If you're imagining running it as the backbone of a 500-person company's operations, you're going to have a bad time. The architecture is optimized for self-hosted, single-instance deployments. That's a feature for privacy and simplicity, but a constraint for scale."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{children:"Security requires active attention."})," You're giving an agent access to your terminal, your browser, your email. The self-hosted model means your data stays on your infrastructure, which is good. But it also means ",s.jsx("strong",{children:"you"})," are responsible for securing that infrastructure. There are ",s.jsx("a",{href:"https://mysummit.school/blog/clawdbot-kriticheskiy-analiz/",className:"text-octo-purple hover:underline",children:"documented concerns"})," about the security surface area of running autonomous agents with broad system access. Treat this like you'd treat any tool with production credentials: principle of least privilege, audit logs, and regular reviews of what the agent can actually reach."]}),s.jsx("blockquote",{className:"border-l-4 border-octo-purple pl-4 italic text-octo-text my-6",children:`You're not outsourcing the security problem. You're just moving it from "do I trust this vendor?" to "do I trust my own infrastructure?"`}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:[s.jsx("strong",{children:'The "autonomous" label is aspirational.'})," In practice, most teams we've talked to — including us — run OpenClaw in a supervised mode where it prepares actions for human approval rather than executing independently. That's probably where most people should start."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"when it makes sense, and when it doesn't"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["OpenClaw makes sense when you have ",s.jsx("strong",{children:"repetitive, well-defined workflows"})," that currently depend on human attention but not human judgment. CI/CD monitoring. Expense processing. Data extraction from websites without APIs. These are tasks where the cost of occasional errors is low and the cost of human time is high."]}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["It makes less sense when the task requires ",s.jsx("strong",{children:"contextual judgment that changes frequently"}),", when the stakes of an error are significant, or when you're trying to replace a proper tool with an AI agent and some duct tape."]}),s.jsx("h2",{className:"text-2xl font-bold mt-12 mb-4",children:"want to skip the self-hosting?"}),s.jsxs("p",{className:"text-octo-text leading-relaxed mb-4",children:["Setting up and maintaining your own OpenClaw instance is fine if you enjoy that sort of thing. If you'd rather just get the automation running, we built ",s.jsx("strong",{children:s.jsx("a",{href:"https://octoclaw.ai/?utm_source=octomindBlog",className:"text-octo-purple hover:underline",children:"OctoClaw"})})," — a hosted OpenClaw instance so you can start building workflows like the ones above without managing the infrastructure yourself."]}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-8",children:s.jsx("a",{href:"https://octoclaw.ai/?utm_source=octomindBlog",className:"text-octo-purple hover:underline font-bold",children:"Try OctoClaw →"})}),s.jsx("p",{className:"text-octo-text leading-relaxed mb-4",children:"We're still early in our OctoClaw experiment. Some of it is working well. Some of it will probably break in ways we haven't anticipated. When it does, maybe that'll be its own blog post."}),s.jsx("p",{className:"text-octo-text leading-relaxed italic",children:"— The Octomind team"})]}),s.jsx(Ee,{name:"Daniel Roedler",title:"Co-founder and CPO at Octomind",imageSrc:"/assets/blogs/danielr-close-up.webp"}),s.jsx(ge,{})]})]})}),s.jsx(se,{})]})}const TR=[{question:"Why shouldn't I just ask a coding agent to write my Playwright tests?",answer:"You absolutely can, and they will work on Day 1. The problem starts on Day 2. When your UI evolves, hydration logic changes, or a selector breaks, you have to manually debug and re-prompt the agent for every single failure. Octomind handles this entire lifecycle automatically—detecting changes, analyzing the failure trace, and self-healing the test definition without your intervention."},{question:"Am I locked into a proprietary platform?",answer:'No. Unlike "no-code" tools that store tests in a black box, Octomind generates standard, open-source Playwright code. You can execute these tests locally, debug them in VS Code, or export them entirely if you ever decide to leave. We believe the value is in the management of the tests, not in holding the code hostage.'},{question:"How is your AI different from a generic LLM like ChatGPT or Claude?",answer:`A generic agent only sees code (text). It guesses based on syntax. Octomind's AI has runtime context—it "sees" the browser execution trace, the DOM snapshots, and the network logs during the test run. This allows it to understand why a test failed (e.g., "The button is covered by a cookie banner" vs. "The button doesn't exist") and fix it accurately, which a text-based agent simply cannot do.`},{question:"Do I have to manually prompt Octomind to generate tests?",answer:`No. This is the difference between a "tool" and a "platform." Octomind integrates directly into your CI/CD pipeline. When you merge a Pull Request, we proactively scan your deployment for new routes or broken flows and generate the necessary tests automatically. We watch your app so you don't have to remember to test it.`},{question:"AI tests are often flaky. How do you prevent that?",answer:'Raw Playwright scripts generated by LLMs often miss complex wait conditions (like hydration or network idleness), leading to flake. Octomind runs your tests on a managed grid with "batteries-included" infrastructure: smart auto-retries, advanced waiting logic for modern frameworks (Next.js, Qwik), and environment stabilization. We provide the reliable runtime that raw scripts lack.'}];function IR(){const e=Fn();return s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsxs("main",{className:"pt-32 pb-20 relative z-10",children:[s.jsxs("section",{className:"max-w-7xl mx-auto px-4 md:px-6",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsxs("h1",{className:"text-4xl md:text-5xl lg:text-6xl font-bold mb-6 leading-tight",children:[s.jsx("span",{className:"bg-gradient-to-r from-octo-purple to-octo-teal bg-clip-text text-transparent",children:"code generation is solved"}),s.jsx("br",{}),s.jsx("span",{className:"text-white",children:"lifecycle management is not"})]}),s.jsxs("p",{className:"text-lg md:text-xl text-octo-text max-w-3xl mx-auto",children:["Coding agents write great scripts. But who maintains them?",s.jsx("br",{className:"hidden sm:inline"}),"Octomind wraps AI-generated tests in a self-healing, managed platform."]})]}),s.jsxs("div",{className:"relative max-w-5xl mx-auto mb-16",children:[s.jsxs("div",{className:"absolute inset-0 pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/2 left-1/4 w-48 h-48 bg-octo-purple/15 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute top-1/2 right-1/4 w-64 h-64 bg-octo-teal/15 rounded-full blur-3xl"})]}),s.jsxs("div",{className:"grid md:grid-cols-2 gap-8 lg:gap-16 items-end",children:[s.jsxs("div",{className:"relative",children:[s.jsxs("div",{className:"text-center mb-6",children:[s.jsx("span",{className:"text-sm text-octo-text/60 uppercase tracking-wider",children:"Coding Agent"}),s.jsxs("div",{className:"flex items-center justify-center gap-2 mt-1",children:[s.jsx("span",{className:"text-2xl font-bold text-white",children:"10%"}),s.jsx("span",{className:"text-sm text-octo-text",children:"of the solution"})]})]}),s.jsxs("div",{className:"relative mx-auto w-full max-w-[280px]",children:[s.jsx("div",{className:"h-32 md:h-40 relative",children:s.jsxs("svg",{className:"absolute inset-0 w-full h-full opacity-20",viewBox:"0 0 200 100",children:[s.jsx("line",{x1:"20",y1:"20",x2:"180",y2:"20",stroke:"white",strokeDasharray:"4 4",strokeWidth:"1"}),s.jsx("line",{x1:"20",y1:"50",x2:"180",y2:"50",stroke:"white",strokeDasharray:"4 4",strokeWidth:"1"}),s.jsx("line",{x1:"20",y1:"80",x2:"180",y2:"80",stroke:"white",strokeDasharray:"4 4",strokeWidth:"1"})]})}),s.jsxs("div",{className:"relative group",children:[s.jsx("div",{className:"absolute -top-3 -right-3 z-10",children:s.jsx("div",{className:"bg-yellow-500 text-yellow-950 text-[10px] font-semibold px-2 py-1 rounded-md shadow-lg transform rotate-3",children:"⚠ Last updated: 3 months ago"})}),s.jsx("div",{className:"relative bg-gradient-to-br from-white/10 to-white/5 border border-white/20 rounded-lg px-6 py-3 backdrop-blur-sm",children:s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(ta,{className:"w-5 h-5 text-white/60"}),s.jsx("span",{className:"text-sm font-medium text-white/80",children:"Playwright Script"})]})})]}),s.jsx("div",{className:"mt-4 text-center",children:s.jsx("span",{className:"text-xs text-white/40 italic",children:"fragile • no retry • no infrastructure"})})]})]}),s.jsxs("div",{className:"relative",children:[s.jsxs("div",{className:"text-center mb-6",children:[s.jsx("span",{className:"text-sm text-octo-purple uppercase tracking-wider font-medium",children:"Octomind"}),s.jsxs("div",{className:"flex items-center justify-center gap-2 mt-1",children:[s.jsx("span",{className:"text-2xl font-bold text-white",children:"100%"}),s.jsx("span",{className:"text-sm text-octo-text",children:"complete platform"})]})]}),s.jsxs("div",{className:"relative mx-auto w-full max-w-[280px] space-y-3",children:[s.jsx("div",{className:"relative group",children:s.jsx("div",{className:"relative bg-gradient-to-br from-slate-600/80 to-slate-700/80 border border-slate-500/30 rounded-lg px-5 py-4",children:s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(qv,{className:"w-5 h-5 text-slate-300"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-sm font-medium text-white",children:"Managed Browser Grid"}),s.jsx("p",{className:"text-xs text-slate-400",children:"Parallel execution & scaling"})]})]})})}),s.jsxs("div",{className:"relative group",children:[s.jsx("div",{className:"relative bg-gradient-to-br from-octo-purple/90 to-octo-purple-dark/90 border border-octo-purple-light/40 rounded-lg px-5 py-4",style:{boxShadow:"0 0 40px rgba(92,91,219,0.4), 0 8px 32px rgba(92,91,219,0.3)"},children:s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(Vv,{className:"w-5 h-5 text-white"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-sm font-medium text-white",children:"Auto-Healing & Smart Waits"}),s.jsx("p",{className:"text-xs text-octo-purple-light",children:"Self-repair on failure"})]})]})}),s.jsx("div",{className:"absolute inset-0 rounded-lg bg-octo-purple/20 blur-xl -z-10"})]}),s.jsx("div",{className:"relative group",children:s.jsx("div",{className:"relative bg-gradient-to-br from-octo-teal/20 to-octo-teal/10 border border-octo-teal/40 rounded-lg px-5 py-4",children:s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(ta,{className:"w-5 h-5 text-octo-teal"}),s.jsxs("div",{children:[s.jsx("span",{className:"text-sm font-medium text-white",children:"Playwright Script"}),s.jsx("p",{className:"text-xs text-octo-teal/80",children:"Standard, portable code"})]})]})})})]})]})]}),s.jsxs("div",{className:"hidden md:flex items-center justify-center mt-12 gap-4",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:"w-16 h-px bg-white/20"}),s.jsx("span",{className:"text-xs text-white/40 whitespace-nowrap",children:"Agent provides"}),s.jsx("div",{className:"w-3 h-px bg-white/20"})]}),s.jsx("div",{className:"relative px-4 py-2 rounded-full border border-white/10 bg-white/5",children:s.jsxs("span",{className:"text-sm font-medium text-white",children:["Only ",s.jsx("span",{className:"text-octo-purple",children:"10%"})," vs ",s.jsx("span",{className:"text-octo-teal",children:"100%"})]})}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:"w-3 h-px bg-white/20"}),s.jsx("span",{className:"text-xs text-white/40 whitespace-nowrap",children:"Octomind provides"}),s.jsx("div",{className:"w-16 h-px bg-white/20"})]})]}),s.jsx("div",{className:"md:hidden mt-8 text-center",children:s.jsx("div",{className:"inline-flex items-center gap-2 px-4 py-2 rounded-full border border-white/10 bg-white/5",children:s.jsxs("span",{className:"text-sm text-white",children:["Agent: ",s.jsx("span",{className:"text-white/60",children:"10%"})," → Octomind: ",s.jsx("span",{className:"text-octo-teal",children:"100%"})]})})})]}),s.jsxs("div",{className:"flex flex-col sm:flex-row items-center justify-center gap-4 mb-24",children:[s.jsx(ut,{className:"px-8 py-6 text-base font-medium rounded-xl bg-octo-purple hover:bg-octo-purple-dark text-white shadow-lg shadow-octo-purple/25",onClick:()=>e("/pricing"),children:"TRY FOR FREE"}),s.jsx(ut,{variant:"outline",className:"px-8 py-6 text-base font-medium rounded-xl bg-transparent border-white/20 text-white hover:bg-white/10 hover:border-white/30",onClick:()=>window.open("https://octomind.dev/docs/dev-mode/dev-mode","_blank"),children:"See Docs"})]})]}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx(Ol,{}),s.jsxs("section",{className:"max-w-7xl mx-auto px-4 md:px-6 mt-32",children:[s.jsxs("div",{className:"text-center mb-12",children:[s.jsx("p",{className:"text-sm text-octo-purple font-medium mb-4 tracking-wide uppercase",children:"Continuous Discovery"}),s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold mb-6",children:"the only agent that lives in your PRs"}),s.jsx("p",{className:"text-lg text-octo-text max-w-3xl mx-auto",children:"Stop manually prompting for tests. When you merge, Octomind scans your deployment, discovers new flows, and opens a PR with the generated tests automatically."})]}),s.jsx("div",{className:"max-w-2xl mx-auto",children:s.jsxs("div",{className:"bg-[#0d1117] rounded-xl border border-[#30363d] overflow-hidden",children:[s.jsxs("div",{className:"flex items-center gap-3 px-4 py-3 bg-[#161b22] border-b border-[#30363d]",children:[s.jsx(J4,{className:"w-5 h-5 text-octo-green"}),s.jsx("span",{className:"text-sm font-medium text-white",children:"Add generated E2E tests for checkout flow"}),s.jsx("span",{className:"ml-auto text-xs text-[#8b949e] bg-[#238636]/20 text-[#3fb950] px-2 py-0.5 rounded-full",children:"Open"})]}),s.jsx("div",{className:"p-4",children:s.jsxs("div",{className:"flex gap-3",children:[s.jsx("div",{className:"w-10 h-10 rounded-full bg-octo-purple/20 border border-octo-purple/30 flex items-center justify-center flex-shrink-0 p-1.5",children:s.jsx("img",{src:qf,alt:"Octomind",className:"w-full h-full object-contain"})}),s.jsx("div",{className:"flex-1",children:s.jsxs("div",{className:"bg-[#161b22] border border-[#30363d] rounded-lg overflow-hidden",children:[s.jsxs("div",{className:"px-4 py-2 bg-[#1c2128] border-b border-[#30363d] flex items-center gap-2",children:[s.jsx("span",{className:"font-medium text-sm text-white",children:"octomind-bot"}),s.jsx("span",{className:"text-xs text-[#8b949e]",children:"commented 1 minute ago"})]}),s.jsxs("div",{className:"px-4 py-3 text-sm text-[#c9d1d9]",children:[s.jsxs("p",{className:"mb-3",children:["🔍 I detected a new flow in ",s.jsx("code",{className:"bg-[#343942] px-1.5 py-0.5 rounded text-octo-teal",children:"/checkout"}),"."]}),s.jsxs("p",{children:["I have generated a new test case: ",s.jsx("code",{className:"bg-[#343942] px-1.5 py-0.5 rounded text-octo-teal",children:"checkout-guest.spec.ts"})]})]})]})})]})}),s.jsx("div",{className:"px-4 pb-4",children:s.jsx("div",{className:"bg-[#161b22] border border-[#30363d] rounded-lg p-3",children:s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(ON,{className:"w-5 h-5 text-[#3fb950]"}),s.jsxs("div",{className:"flex-1",children:[s.jsx("span",{className:"text-sm font-medium text-white",children:"Octomind / E2E Tests"}),s.jsx("span",{className:"text-xs text-[#3fb950] ml-2",children:"Passed"})]}),s.jsx("span",{className:"text-xs text-[#8b949e]",children:"in 2m 34s"})]})})})]})})]}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6 mt-16",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsxs("section",{className:"max-w-7xl mx-auto px-4 md:px-6 mt-16",children:[s.jsxs("div",{className:"text-center mb-12",children:[s.jsx("p",{className:"text-sm text-octo-purple font-medium mb-4 tracking-wide uppercase",children:"Infrastructure & Stability"}),s.jsxs("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold mb-6",children:["it works on your machine",s.jsx("br",{}),s.jsx("span",{className:"text-octo-text",children:"we make sure it works everywhere"})]}),s.jsx("p",{className:"text-lg text-octo-text max-w-3xl mx-auto",children:"Raw Playwright scripts are brittle. They fail on network jitters and hydration issues. Octomind's runtime wraps your tests with smart waits, auto-retries, and trace recording—batteries included."})]}),s.jsxs("div",{className:"max-w-3xl mx-auto relative",children:[s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"bg-[#1a1d2e] border border-white/10 rounded-xl p-5 flex items-center gap-4 min-h-[88px]",children:[s.jsx("div",{className:"w-12 h-12 rounded-lg bg-white/5 flex items-center justify-center flex-shrink-0",children:s.jsx(ta,{className:"w-6 h-6 text-white"})}),s.jsxs("div",{children:[s.jsx("h4",{className:"font-medium text-white",children:"Standard Playwright Code"}),s.jsx("p",{className:"text-sm text-octo-text",children:"Your test logic, portable and readable"})]})]}),s.jsxs("div",{className:"relative",children:[s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"bg-gradient-to-r from-octo-purple/10 to-octo-teal/10 border border-octo-purple/30 rounded-xl p-5 flex items-center gap-4 min-h-[88px]",children:[s.jsx("div",{className:"w-12 h-12 rounded-lg bg-octo-purple/20 flex items-center justify-center flex-shrink-0",children:s.jsx(Vv,{className:"w-6 h-6 text-octo-purple"})}),s.jsxs("div",{children:[s.jsx("h4",{className:"font-medium text-white",children:"Smart Wait & Retry Logic"}),s.jsx("p",{className:"text-sm text-octo-text",children:"Handles flakiness, hydration, network delays"})]})]}),s.jsxs("div",{className:"bg-gradient-to-r from-octo-purple/10 to-octo-teal/10 border border-octo-purple/30 rounded-xl p-5 flex items-center gap-4 min-h-[88px]",children:[s.jsx("div",{className:"w-12 h-12 rounded-lg bg-octo-teal/20 flex items-center justify-center flex-shrink-0",children:s.jsx(qv,{className:"w-6 h-6 text-octo-teal"})}),s.jsxs("div",{children:[s.jsx("h4",{className:"font-medium text-white",children:"Managed Browser Grid"}),s.jsx("p",{className:"text-sm text-octo-text",children:"Parallel execution, trace recording, scaling"})]})]})]}),s.jsxs("div",{className:"hidden md:flex items-center gap-2 absolute left-full top-0 bottom-0 ml-6",children:[s.jsxs("svg",{viewBox:"0 0 20 100",className:"w-5 h-full",fill:"none",xmlns:"http://www.w3.org/2000/svg",preserveAspectRatio:"none",children:[s.jsx("path",{d:"M 2 2 Q 16 2 16 50 Q 16 98 2 98",stroke:"url(#bracketGradient2)",strokeWidth:"2",strokeLinecap:"round",fill:"none"}),s.jsx("defs",{children:s.jsxs("linearGradient",{id:"bracketGradient2",x1:"0%",y1:"0%",x2:"0%",y2:"100%",children:[s.jsx("stop",{offset:"0%",stopColor:"#5c5bdb"}),s.jsx("stop",{offset:"100%",stopColor:"#00AF93"})]})})]}),s.jsx("div",{className:"px-4 py-3 rounded-lg bg-gradient-to-r from-octo-purple/20 to-octo-teal/20 border border-octo-purple/40",children:s.jsx("p",{className:"text-sm font-semibold text-transparent bg-clip-text bg-gradient-to-r from-octo-purple to-octo-teal whitespace-nowrap",children:"What Coding Agents miss"})})]})]})]}),s.jsx("div",{className:"md:hidden mt-6 text-center",children:s.jsx("div",{className:"inline-flex items-center gap-2 bg-octo-purple/10 border border-octo-purple/30 rounded-lg px-4 py-2",children:s.jsx("span",{className:"text-sm text-octo-purple font-medium",children:"↑ What Coding Agents miss"})})})]})]}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6 mt-16",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsxs("section",{id:"comparison",className:"max-w-7xl mx-auto px-4 md:px-6 mt-16",children:[s.jsxs("div",{className:"text-center mb-12",children:[s.jsx("p",{className:"text-sm text-octo-purple font-medium mb-4 tracking-wide uppercase",children:"Build vs. Buy"}),s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold mb-6",children:"the full picture, at a glance"})]}),s.jsx("div",{className:"max-w-4xl mx-auto overflow-x-auto",children:s.jsxs("table",{className:"w-full",children:[s.jsx("thead",{children:s.jsxs("tr",{className:"border-b border-white/10",children:[s.jsx("th",{className:"py-4 px-4 text-left text-sm font-normal text-octo-text"}),s.jsxs("th",{className:"py-4 px-6 text-center",children:[s.jsx("div",{className:"text-sm font-medium text-octo-text",children:"Coding Agent"}),s.jsx("div",{className:"text-xs text-octo-text/60",children:"(DIY)"})]}),s.jsx("th",{className:"py-4 px-6 text-center bg-octo-purple/10 rounded-t-xl",children:s.jsx("div",{className:"text-sm font-medium text-white",children:"Octomind Platform"})})]})}),s.jsx("tbody",{className:"text-sm",children:[{feature:"Discovery",diy:"Manual Prompting",octo:"Auto-Discovery on Merge"},{feature:"Maintenance",diy:"Manual Rewrite",octo:"Self-Healing AI"},{feature:"Infrastructure",diy:"You manage CI shards",octo:"Zero-Config Cloud Grid"},{feature:"Debugging",diy:"Console Logs",octo:"Interactive Trace Viewer"},{feature:"Ownership",diy:"Your Code",octo:"Your Code (Export anytime)"}].map((t,n,r)=>s.jsxs("tr",{className:"border-b border-white/5",children:[s.jsx("td",{className:"py-4 px-4 font-medium text-white",children:t.feature}),s.jsx("td",{className:"py-4 px-6 text-center text-octo-text/60",children:t.diy}),s.jsx("td",{className:`py-4 px-6 text-center bg-octo-purple/10 ${n===r.length-1?"rounded-b-xl":""}`,children:s.jsx("span",{className:"text-octo-green font-medium",children:t.octo})})]},t.feature))})]})})]}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6 mt-16",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"})}),s.jsx("section",{id:"faq",className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"mb-6 text-4xl md:text-5xl font-black text-white",children:"frequently asked questions"}),s.jsx("p",{className:"text-xl font-normal text-white",children:"Everything you need to know about Octomind vs. coding agents"})]}),s.jsx("div",{className:"space-y-4",children:TR.map((t,n)=>s.jsxs("details",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden hover:border-white/20 transition-all duration-300",children:[s.jsxs("summary",{className:"w-full px-6 md:px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden",children:[s.jsx("span",{className:"pr-8 text-sm font-normal text-white",children:t.question}),s.jsx(Al,{className:"w-6 h-6 text-octo-purple flex-shrink-0 transition-transform duration-300 group-open:rotate-180",strokeWidth:2})]}),s.jsx("div",{className:"px-6 md:px-8 pb-6 leading-relaxed text-sm font-normal text-white",children:t.answer})]},n))})]})})]}),s.jsx(se,{})]})}const AR=[{question:"Which AI editors are supported?",answer:"The Octomind MCP server follows the open Model Context Protocol standard. It works natively with Cursor, Windsurf, and Claude Desktop. Any future client that implements MCP will also work out of the box."},{question:"Can I test localhost apps from my IDE?",answer:"Yes. Since the MCP server runs locally on your machine, it can access your localhost server directly. You can trigger runs against localhost:3000 to verify changes before you even push to staging."},{question:`Why shouldn't I just let the agent "drive" the browser?`,answer:'Agents acting as "drivers" (clicking coordinates) are flaky and slow. Octomind treats the agent as an Engineer, not a user. Your agent generates the code, and our deterministic engine handles the execution, ensuring your tests are stable and repeatable.'},{question:"Can I use this to verify code my agent just wrote?",answer:'Absolutely. We recommend the "Adversarial Agent" pattern: use one agent instance to write the feature code, and use the Octomind MCP tool to spin up a separate "QA Agent" to independently verify that code with a fresh test case.'},{question:"How do I debug a failed test?",answer:`You don't need to open a dashboard. Just ask the agent to "Fetch the trace for the last failure." It will pull the screenshots, error logs, and DOM snapshots directly into your chat window so you can fix the issue without ever leaving your editor.`}],PR=()=>s.jsx("section",{className:"py-24 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsxs("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-4",children:["chain Octomind with ",s.jsx("span",{className:"text-transparent bg-clip-text bg-gradient-to-r from-octo-purple to-octo-blue",children:"Jira & GitHub"})]}),s.jsxs("p",{className:"text-xl text-octo-text",children:["Don't just chat. ",s.jsx("span",{className:"text-white",children:"Execute."})," MCP allows agents to pipe data between tools"]})]}),s.jsxs("div",{className:"relative mb-12",children:[s.jsx("div",{className:"hidden md:block absolute top-1/2 left-[15%] right-[15%] h-[2px] -translate-y-1/2 z-0",children:s.jsx("div",{className:"w-full h-full bg-gradient-to-r from-octo-blue/50 via-octo-purple to-octo-teal/50"})}),s.jsxs("div",{className:"grid md:grid-cols-3 gap-6 relative z-10",children:[s.jsxs("div",{className:"p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 backdrop-blur-sm hover:border-white/20 transition-all",children:[s.jsxs("div",{className:"flex items-center justify-between mb-4",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-[#0052CC]/20 flex items-center justify-center",children:s.jsx("svg",{className:"w-6 h-6 text-[#0052CC]",viewBox:"0 0 24 24",fill:"currentColor",children:s.jsx("path",{d:"M11.571 11.513H0a5.218 5.218 0 0 0 5.232 5.215h2.13v2.057A5.215 5.215 0 0 0 12.575 24V12.518a1.005 1.005 0 0 0-1.005-1.005zm5.723-5.756H5.736a5.215 5.215 0 0 0 5.215 5.214h2.129v2.058a5.218 5.218 0 0 0 5.215 5.214V6.758a1.001 1.001 0 0 0-1.001-1.001zM23.013 0H11.455a5.215 5.215 0 0 0 5.215 5.215h2.129v2.057A5.215 5.215 0 0 0 24 12.483V1.005A1.005 1.005 0 0 0 23.013 0z"})})}),s.jsx("span",{className:"text-xs font-mono text-white/40 px-2 py-1 bg-white/5 rounded",children:"INPUT"})]}),s.jsx("p",{className:"font-mono text-sm text-octo-text mb-2",children:"Jira MCP"}),s.jsx("code",{className:"text-white font-mono text-base",children:'"Fetch Ticket-102"'}),s.jsx("div",{className:"md:hidden flex justify-center mt-4",children:s.jsx(zv,{className:"w-6 h-6 text-octo-purple rotate-90"})})]}),s.jsxs("div",{className:"relative p-6 rounded-2xl bg-[#1e1e1e] border border-octo-purple/50 backdrop-blur-sm",children:[s.jsx("div",{className:"absolute -inset-1 bg-octo-purple/20 rounded-2xl blur-xl pointer-events-none"}),s.jsxs("div",{className:"relative",children:[s.jsxs("div",{className:"flex items-center justify-between mb-4",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-purple/20 flex items-center justify-center p-2 border border-octo-purple/30",children:s.jsx("img",{src:qf,alt:"Octomind",className:"w-full h-full object-contain"})}),s.jsx("span",{className:"text-xs font-mono text-octo-purple-light px-2 py-1 bg-octo-purple/10 rounded border border-octo-purple/30",children:"PROCESS"})]}),s.jsx("p",{className:"font-mono text-sm text-octo-text mb-2",children:"Octomind MCP"}),s.jsx("code",{className:"text-octo-purple-light font-mono text-base",children:'"Generate Test Case"'})]}),s.jsx("div",{className:"md:hidden flex justify-center mt-4",children:s.jsx(zv,{className:"w-6 h-6 text-octo-purple rotate-90"})})]}),s.jsxs("div",{className:"p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 backdrop-blur-sm hover:border-white/20 transition-all",children:[s.jsxs("div",{className:"flex items-center justify-between mb-4",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-teal/20 flex items-center justify-center",children:s.jsx(ta,{className:"w-6 h-6 text-octo-teal"})}),s.jsx("span",{className:"text-xs font-mono text-octo-teal px-2 py-1 bg-octo-teal/10 rounded border border-octo-teal/30",children:"OUTPUT"})]}),s.jsx("p",{className:"font-mono text-sm text-octo-text mb-2",children:"Local Filesystem"}),s.jsx("code",{className:"text-octo-teal font-mono text-sm",children:'"Login flow test generated"'})]})]})]})]})}),OR=`{ "mcpServers": { "octomind": { "command": "npx", "args": ["@octomind/octomind-mcp"], "env": { "APIKEY": "" } } } }`,DR=()=>s.jsx("section",{className:"pt-32 pb-20 relative overflow-hidden",children:s.jsx("div",{className:"max-w-7xl mx-auto px-4 md:px-6 relative z-10",children:s.jsxs("div",{className:"flex flex-col items-center text-center",children:[s.jsxs("h1",{className:"text-4xl md:text-5xl lg:text-6xl font-bold mb-6 max-w-4xl",children:[s.jsx("span",{className:"text-white",children:"remote control"}),s.jsx("br",{}),s.jsx("span",{className:"bg-gradient-to-r from-octo-purple via-octo-blue to-octo-teal bg-clip-text text-transparent",children:"Octomind from your IDE"})]}),s.jsx("p",{className:"text-xl text-octo-text max-w-3xl mb-10",children:"Stay in your workflow. Connect your AI agent (Cursor, Claude, etc.) to Octomind via MCP to generate, run, and debug tests without ever leaving your editor."}),s.jsx("div",{className:"w-full max-w-4xl relative",children:s.jsx("div",{className:"backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl p-2 md:p-4 hover:border-white/20 transition-all",children:s.jsxs("div",{className:"bg-[#1e1e1e] rounded-xl overflow-hidden border border-white/10",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[#323233] border-b border-white/5",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("div",{className:"flex gap-1.5",children:[s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#ff5f57]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#febc2e]"}),s.jsx("div",{className:"w-3 h-3 rounded-full bg-[#28c840]"})]}),s.jsxs("div",{className:"flex items-center gap-2 ml-2 px-3 py-1.5 rounded bg-[#1e1e1e] text-white/90",children:[s.jsx("span",{className:"text-xs text-[#e6a700]",children:"{ }"}),s.jsx("span",{className:"text-xs font-mono",children:"~/.claude/config.json"})]})]}),s.jsx(kn,{textToCopy:OR})]}),s.jsx("div",{className:"p-4 overflow-x-auto",children:s.jsx("pre",{className:"text-xs md:text-sm font-mono leading-relaxed",children:s.jsxs("code",{children:[s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"1"}),s.jsx("span",{className:"text-white/80",children:"{"})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"2"}),s.jsx("span",{className:"text-octo-purple-light",children:' "mcpServers"'}),s.jsxs("span",{className:"text-white/80",children:[": ","{"]})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"3"}),s.jsx("span",{className:"text-octo-blue",children:' "octomind"'}),s.jsxs("span",{className:"text-white/80",children:[": ","{"]})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"4"}),s.jsx("span",{className:"text-white/50",children:' "command"'}),s.jsx("span",{className:"text-white/80",children:": "}),s.jsx("span",{className:"text-[#98c379]",children:'"npx"'}),s.jsx("span",{className:"text-white/80",children:","})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"5"}),s.jsx("span",{className:"text-white/50",children:' "args"'}),s.jsx("span",{className:"text-white/80",children:": ["}),s.jsx("span",{className:"text-[#98c379]",children:'"@octomind/octomind-mcp"'}),s.jsx("span",{className:"text-white/80",children:"],"})]}),s.jsxs("div",{className:"flex items-center",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"6"}),s.jsx("span",{className:"text-white/50",children:' "env"'}),s.jsxs("span",{className:"text-white/80",children:[": ","{"]})]}),s.jsxs("div",{className:"flex items-center bg-yellow-500/10 -mx-4 px-4 border-l-2 border-yellow-400",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"7"}),s.jsx("span",{className:"text-yellow-400",children:' "APIKEY"'}),s.jsx("span",{className:"text-white/80",children:": "}),s.jsx("span",{className:"text-yellow-300",children:'""'}),s.jsx("span",{className:"ml-2 px-2 py-0.5 text-xs bg-yellow-500/20 text-yellow-400 rounded font-sans",children:"← Add your key"})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"8"}),s.jsxs("span",{className:"text-white/80",children:[" ","}"]})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"9"}),s.jsxs("span",{className:"text-white/80",children:[" ","}"]})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"10"}),s.jsxs("span",{className:"text-white/80",children:[" ","}"]})]}),s.jsxs("div",{className:"flex",children:[s.jsx("span",{className:"text-white/30 w-6 text-right mr-4 select-none",children:"11"}),s.jsx("span",{className:"text-white/80",children:"}"})]})]})})})]})})}),s.jsxs("div",{className:"flex flex-col sm:flex-row items-center justify-center gap-4 mt-10",children:[s.jsx(ut,{className:"px-8 py-6 text-base font-medium rounded-xl bg-octo-purple hover:bg-octo-purple-dark text-white shadow-lg shadow-octo-purple/25",onClick:()=>window.open("https://octomind.dev/docs/integrations/github-new-flow#create-an-api-key","_blank"),children:"Get API Key"}),s.jsx(ut,{variant:"outline",className:"px-8 py-6 text-base font-medium rounded-xl bg-transparent border-white/20 text-white hover:bg-white/10 hover:border-white/30",onClick:()=>window.open("https://octomind.dev/docs/mcp/install-octomind-mcp","_blank"),children:"See Docs"})]})]})})}),RR=()=>s.jsx("section",{className:"py-24 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12",children:[s.jsx("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-4",children:"watch the agent at work"}),s.jsx("p",{className:"text-xl text-octo-text",children:"From Jira ticket to passing test in one prompt"})]}),s.jsx("div",{className:"w-full max-w-5xl mx-auto",children:s.jsx("div",{className:"rounded-2xl overflow-hidden border border-white/10 hover:border-white/20 transition-all",children:s.jsx("div",{className:"aspect-video",children:s.jsx("iframe",{className:"w-full h-full",src:"https://www.youtube.com/embed/71zh5vWwfb4",title:"Octomind MCP Server Demo",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0})})})}),s.jsx("p",{className:"text-center text-octo-text mt-6 max-w-2xl mx-auto",children:"Daniel Roedler demonstrates how to chain Jira, and GitHub and Octomind into a single prompt using Cursor."})]})});function MR(){return Fn(),s.jsxs("div",{className:"min-h-screen bg-octo-dark text-white",children:[s.jsxs("div",{className:"fixed inset-0 overflow-hidden pointer-events-none",children:[s.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-octo-purple/10 rounded-full blur-3xl"}),s.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-octo-blue/10 rounded-full blur-3xl"})]}),s.jsx(te,{}),s.jsxs("main",{className:"relative z-10",children:[s.jsx(DR,{}),s.jsx(Ol,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx(RR,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx(PR,{}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{className:"py-24 px-4 md:px-6",children:s.jsxs("div",{className:"max-w-6xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-16",children:[s.jsxs("h2",{className:"text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-6",children:["deploy a QA team",s.jsx("br",{}),s.jsx("span",{className:"text-transparent bg-clip-text bg-gradient-to-r from-octo-purple to-octo-teal",children:"inside your IDE"})]}),s.jsx("p",{className:"text-lg text-octo-text max-w-3xl mx-auto",children:"Octomind's MCP server lets you run a specialized 'Tester Agent' alongside your 'Coder Agent', keeping your focus on the feature while we break it."})]}),s.jsxs("div",{className:"grid md:grid-cols-2 gap-6",children:[s.jsxs("div",{className:"relative p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 hover:border-white/20 transition-all overflow-hidden group",children:[s.jsx("div",{className:"absolute -bottom-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity",children:s.jsx(Uv,{className:"w-32 h-32 text-octo-purple-light"})}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-purple/20 flex items-center justify-center mb-5",children:s.jsx(Uv,{className:"w-6 h-6 text-octo-purple-light"})}),s.jsx("h3",{className:"text-xl font-bold text-white mb-3",children:"Never Leave VS Code"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"Your editor is your command center. Trigger test runs, fetch traces, and generate new specs directly from your chat window. No more context switching to CI dashboards."})]})]}),s.jsxs("div",{className:"relative p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 hover:border-white/20 transition-all overflow-hidden group",children:[s.jsx("div",{className:"absolute -bottom-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity",children:s.jsx(Bv,{className:"w-32 h-32 text-octo-blue"})}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-blue/20 flex items-center justify-center mb-5",children:s.jsx(Bv,{className:"w-6 h-6 text-octo-blue"})}),s.jsx("h3",{className:"text-xl font-bold text-white mb-3",children:"The Adversarial Agent"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"Don't let the agent that wrote the code write the test. Use Octomind to spin up a separate 'QA Agent' that impartially verifies your work, ensuring unbiased coverage."})]})]}),s.jsxs("div",{className:"relative p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 hover:border-white/20 transition-all overflow-hidden group",children:[s.jsx("div",{className:"absolute -bottom-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity",children:s.jsx(Wv,{className:"w-32 h-32 text-octo-teal"})}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-teal/20 flex items-center justify-center mb-5",children:s.jsx(Wv,{className:"w-6 h-6 text-octo-teal"})}),s.jsx("h3",{className:"text-xl font-bold text-white mb-3",children:"Connect the Dots"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"Chain tools together. Ask your agent to 'Read Jira Ticket #123, generate an Octomind test for it, and open a PR in GitHub.' One prompt, three systems connected."})]})]}),s.jsxs("div",{className:"relative p-6 rounded-2xl bg-[#1e1e1e] border border-white/10 hover:border-white/20 transition-all overflow-hidden group",children:[s.jsx("div",{className:"absolute -bottom-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity",children:s.jsx(Hv,{className:"w-32 h-32 text-octo-green"})}),s.jsxs("div",{className:"relative",children:[s.jsx("div",{className:"w-12 h-12 rounded-xl bg-octo-green/20 flex items-center justify-center mb-5",children:s.jsx(Hv,{className:"w-6 h-6 text-octo-green"})}),s.jsx("h3",{className:"text-xl font-bold text-white mb-3",children:"Real Users, Real Browsers"}),s.jsx("p",{className:"text-octo-text text-base leading-relaxed",children:"We don't mock the DOM. Your agent triggers real browsers in our scalable cloud grid that click, type, and interact like actual humans—catching bugs that API tests miss."})]})]})]})]})}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})}),s.jsx("section",{id:"mcp-faq",className:"py-16 md:py-24 px-4 md:px-6 relative",children:s.jsxs("div",{className:"max-w-4xl mx-auto",children:[s.jsxs("div",{className:"text-center mb-12 md:mb-16",children:[s.jsx("h2",{className:"mb-6 text-4xl md:text-5xl font-black text-white",children:"frequently asked questions"}),s.jsx("p",{className:"text-xl font-normal text-white",children:"Everything you need to know about the MCP server"})]}),s.jsx("div",{className:"space-y-4",children:AR.map((e,t)=>s.jsxs("details",{className:"group backdrop-blur-xl bg-octo-card/60 border border-white/10 rounded-2xl overflow-hidden hover:border-white/20 transition-all duration-300",children:[s.jsxs("summary",{className:"w-full px-6 md:px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden",children:[s.jsx("span",{className:"pr-8 text-sm font-normal text-white",children:e.question}),s.jsx("svg",{className:"w-6 h-6 text-octo-purple flex-shrink-0 transition-transform duration-300 group-open:rotate-180",strokeWidth:2,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),s.jsx("div",{className:"px-6 md:px-8 pb-6 leading-relaxed text-sm font-normal text-white",children:e.answer})]},t))})]})}),s.jsx("div",{className:"max-w-5xl mx-auto px-4 md:px-6",children:s.jsx("div",{className:"h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"})})]}),s.jsx(se,{})]})}const Ab=["/","/product/dev-mode","/product/octomind-vs-coding-agents","/product/playwright-self-healing","/product/mcp","/about","/pricing","/blog","/case-studies","/blog/openclaw-in-projects-real-automation-for-people-whod-rather-be-coding","/blog/whos-actually-vibe-coding-the-data-doesnt-match-the-hype","/blog/qa-agent-in-your-ci-cd-pipeline","/blog/where-chatgpt-really-helps-in-test-automation-and-where-it-still-falls-short","/blog/11-enterprise-ready-end-to-end-testing-tools-built-for-scale","/blog/why-agents-do-not-write-most-of-our-code-a-reality-check","/blog/product-update/25-10-23","/blog/what-is-the-octomind-design-system","/blog/software-testing-stack-guide-from-unit-tests-to-performance","/blog/organize-automated-tests-without-getting-eaten-by-your-devs","/case-studies/big-saas-cut-test-maintenance-by-83-percent","/case-studies/big-saas-automation","/case-studies/scaling-qa-to-match-the-speed-of-engineering-innovation","/blog/9-agentic-end-to-end-testing-tools-to-consider-in-2025","/blog/devs-need-e2e-tests","/case-studies/how-to-deliver-sales-coaching-quality-without-slowing-down","/case-studies/how-leaping-ai-ships-faster-with-reliable-e2e-tests-without-hiring-a-qa","/blog/why-we-finally-allowed-arbitrary-waits-in-our-tests","/blog/programmer-yelling-at-the-clouds-about-vibe-coding","/blog/cursor-vs-octomind","/blog/my-mcp-vision-for-octomind","/blog/test-isolation-is-not-fun","/blog/ai-doesnt-belong-in-test-runtime","/blog/testing-is-more-about-setup-than-scripts","/blog/stop-automating-manual-test-cases","/blog/how-to-start-with-e2e-testing-from-scratch-an-octomind-tutorial","/blog/our-problem-with-backlogs","/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents","/blog/did-you-break-your-code-or-is-the-test-flaky","/blog/octomind-raises-4-8-million-to-reinvent-software-testing-with-ai","/blog/the-full-stack-testing-mindset","/blog/why-we-built-an-e2e-testing-tool-on-top-of-playwright","/blog/keep-your-copilot-and-your-code-quality-with-ai-testing","/blog/we-went-viral-with-a-broken-app","/blog/test-your-code","/blog/on-developer-dogma-3-never-ship-on-fridays","/blog/navigating-the-typescript-gymnastics-on-developer-dogma-2","/blog/deconstructing-popular-developer-dogmas-1","/blog/on-type-safety-in-langchain-ts","/blog/testing-pyramid-an-evolutionary-tale","/privacy","/legal"],$R={"/":s.jsx(g8,{}),"/product/dev-mode":s.jsx(PD,{}),"/product/octomind-vs-coding-agents":s.jsx(IR,{}),"/product/playwright-self-healing":s.jsx(OD,{}),"/product/mcp":s.jsx(MR,{}),"/about":s.jsx(y8,{}),"/pricing":s.jsx(CD,{}),"/blog":s.jsx(MD,{}),"/case-studies":s.jsx(LD,{}),"/blog/openclaw-in-projects-real-automation-for-people-whod-rather-be-coding":s.jsx(ER,{}),"/blog/whos-actually-vibe-coding-the-data-doesnt-match-the-hype":s.jsx(SR,{}),"/blog/qa-agent-in-your-ci-cd-pipeline":s.jsx(CR,{}),"/blog/where-chatgpt-really-helps-in-test-automation-and-where-it-still-falls-short":s.jsx(WD,{}),"/blog/11-enterprise-ready-end-to-end-testing-tools-built-for-scale":s.jsx(UD,{}),"/blog/why-agents-do-not-write-most-of-our-code-a-reality-check":s.jsx(HD,{}),"/blog/product-update/25-10-23":s.jsx(qD,{}),"/blog/what-is-the-octomind-design-system":s.jsx(VD,{}),"/blog/software-testing-stack-guide-from-unit-tests-to-performance":s.jsx(GD,{}),"/blog/organize-automated-tests-without-getting-eaten-by-your-devs":s.jsx(YD,{}),"/case-studies/big-saas-cut-test-maintenance-by-83-percent":s.jsx(QD,{}),"/case-studies/big-saas-automation":s.jsx(XD,{}),"/case-studies/scaling-qa-to-match-the-speed-of-engineering-innovation":s.jsx(JD,{}),"/blog/9-agentic-end-to-end-testing-tools-to-consider-in-2025":s.jsx(KD,{}),"/blog/devs-need-e2e-tests":s.jsx(ZD,{}),"/case-studies/how-to-deliver-sales-coaching-quality-without-slowing-down":s.jsx(eR,{}),"/case-studies/how-leaping-ai-ships-faster-with-reliable-e2e-tests-without-hiring-a-qa":s.jsx(tR,{}),"/blog/why-we-finally-allowed-arbitrary-waits-in-our-tests":s.jsx(sR,{}),"/blog/programmer-yelling-at-the-clouds-about-vibe-coding":s.jsx(nR,{}),"/blog/cursor-vs-octomind":s.jsx(rR,{}),"/blog/my-mcp-vision-for-octomind":s.jsx(oR,{}),"/blog/test-isolation-is-not-fun":s.jsx(aR,{}),"/blog/ai-doesnt-belong-in-test-runtime":s.jsx(iR,{}),"/blog/testing-is-more-about-setup-than-scripts":s.jsx(lR,{}),"/blog/stop-automating-manual-test-cases":s.jsx(cR,{}),"/blog/how-to-start-with-e2e-testing-from-scratch-an-octomind-tutorial":s.jsx(dR,{}),"/blog/our-problem-with-backlogs":s.jsx(uR,{}),"/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents":s.jsx(hR,{}),"/blog/did-you-break-your-code-or-is-the-test-flaky":s.jsx(mR,{}),"/blog/octomind-raises-4-8-million-to-reinvent-software-testing-with-ai":s.jsx(pR,{}),"/blog/the-full-stack-testing-mindset":s.jsx(xR,{}),"/blog/why-we-built-an-e2e-testing-tool-on-top-of-playwright":s.jsx(fR,{}),"/blog/keep-your-copilot-and-your-code-quality-with-ai-testing":s.jsx(gR,{}),"/blog/we-went-viral-with-a-broken-app":s.jsx(vR,{}),"/blog/test-your-code":s.jsx(yR,{}),"/blog/on-developer-dogma-3-never-ship-on-fridays":s.jsx(bR,{}),"/blog/navigating-the-typescript-gymnastics-on-developer-dogma-2":s.jsx(wR,{}),"/blog/deconstructing-popular-developer-dogmas-1":s.jsx(jR,{}),"/blog/on-type-safety-in-langchain-ts":s.jsx(NR,{}),"/blog/testing-pyramid-an-evolutionary-tale":s.jsx(kR,{}),"/privacy":s.jsx(FD,{}),"/legal":s.jsx(zD,{})},LR=[...Ab.map(e=>({path:e,element:$R[e]})),...Ab.filter(e=>e!=="/").map(e=>({path:`${e}/index.html`,element:s.jsx(ap,{to:e,replace:!0})})),{path:"/privacy-policy",element:s.jsx(ap,{to:"/privacy",replace:!0})},{path:"*",element:s.jsx(BD,{})}],FR=()=>{const e=e8();return s.jsxs(e.BannerContext.Provider,{value:e.visible,children:[e.visible&&s.jsx(t8,{onDismiss:e.dismiss}),s.jsx($A,{}),s.jsx(O3,{children:LR.map(t=>s.jsx(tN,{path:t.path,element:t.element},t.path))}),s.jsx(WA,{})]})};var Ha={};/** * @license React * react-dom-server-legacy.browser.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var x_=f;function ae(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n