From 9a7e99e80f3e8fe151808a3b972065f4bb0cd020 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Tue, 23 Dec 2025 01:35:20 -0800 Subject: [PATCH 01/40] remove dist directory --- dist/DVD_logo.svg | 1 - dist/_redirects | 1 - dist/favicon.svg | 198 ---------------------------------- dist/github-mark.svg | 1 - dist/index-ym20sk5q.js | 59 ----------- dist/index.html | 54 ---------- dist/scratch-logo.svg | 212 ------------------------------------- dist/tailwind-510c5f85.css | 2 - dist/x-logo.svg | 3 - 9 files changed, 531 deletions(-) delete mode 100644 dist/DVD_logo.svg delete mode 100644 dist/_redirects delete mode 100644 dist/favicon.svg delete mode 100644 dist/github-mark.svg delete mode 100644 dist/index-ym20sk5q.js delete mode 100644 dist/index.html delete mode 100644 dist/scratch-logo.svg delete mode 100644 dist/tailwind-510c5f85.css delete mode 100644 dist/x-logo.svg diff --git a/dist/DVD_logo.svg b/dist/DVD_logo.svg deleted file mode 100644 index e85f92a..0000000 --- a/dist/DVD_logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/dist/_redirects b/dist/_redirects deleted file mode 100644 index 0badd56..0000000 --- a/dist/_redirects +++ /dev/null @@ -1 +0,0 @@ -/install.sh https://raw.githubusercontent.com/scratch/scratch/main/install.sh 302 diff --git a/dist/favicon.svg b/dist/favicon.svg deleted file mode 100644 index d350106..0000000 --- a/dist/favicon.svg +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dist/github-mark.svg b/dist/github-mark.svg deleted file mode 100644 index 37fa923..0000000 --- a/dist/github-mark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/dist/index-ym20sk5q.js b/dist/index-ym20sk5q.js deleted file mode 100644 index b13e2e1..0000000 --- a/dist/index-ym20sk5q.js +++ /dev/null @@ -1,59 +0,0 @@ -var bU=Object.create;var{getPrototypeOf:SU,defineProperty:HH,getOwnPropertyNames:NU}=Object;var jU=Object.prototype.hasOwnProperty;var m0=(A,Y,G)=>{G=A!=null?bU(SU(A)):{};let H=Y||!A||!A.__esModule?HH(G,"default",{value:A,enumerable:!0}):G;for(let X of NU(A))if(!jU.call(H,X))HH(H,X,{get:()=>A[X],enumerable:!0});return H};var XH=(A,Y)=>()=>(Y||A((Y={exports:{}}).exports,Y),Y.exports);var zw=(A,Y)=>{for(var G in Y)HH(A,G,{get:Y[G],enumerable:!0,configurable:!0,set:(H)=>Y[G]=()=>H})};var wH=(A,Y)=>()=>(A&&(Y=A(A=0)),Y);var p0=XH((iU)=>{var qH=Symbol.for("react.transitional.element"),CU=Symbol.for("react.portal"),$U=Symbol.for("react.fragment"),xU=Symbol.for("react.strict_mode"),kU=Symbol.for("react.profiler"),_U=Symbol.for("react.consumer"),PU=Symbol.for("react.context"),gU=Symbol.for("react.forward_ref"),hU=Symbol.for("react.suspense"),EU=Symbol.for("react.memo"),Iw=Symbol.for("react.lazy"),yU=Symbol.for("react.activity"),Dw=Symbol.iterator;function vU(A){if(A===null||typeof A!=="object")return null;return A=Dw&&A[Dw]||A["@@iterator"],typeof A==="function"?A:null}var Ow={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Kw=Object.assign,Tw={};function FY(A,Y,G){this.props=A,this.context=Y,this.refs=Tw,this.updater=G||Ow}FY.prototype.isReactComponent={};FY.prototype.setState=function(A,Y){if(typeof A!=="object"&&typeof A!=="function"&&A!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,A,Y,"setState")};FY.prototype.forceUpdate=function(A){this.updater.enqueueForceUpdate(this,A,"forceUpdate")};function bw(){}bw.prototype=FY.prototype;function UH(A,Y,G){this.props=A,this.context=Y,this.refs=Tw,this.updater=G||Ow}var MH=UH.prototype=new bw;MH.constructor=UH;Kw(MH,FY.prototype);MH.isPureReactComponent=!0;var Qw=Array.isArray;function WH(){}var c={H:null,A:null,T:null,S:null},Sw=Object.prototype.hasOwnProperty;function JH(A,Y,G){var H=G.ref;return{$$typeof:qH,type:A,key:Y,ref:H!==void 0?H:null,props:G}}function uU(A,Y){return JH(A.type,Y,A.props)}function RH(A){return typeof A==="object"&&A!==null&&A.$$typeof===qH}function dU(A){var Y={"=":"=0",":":"=2"};return"$"+A.replace(/[=:]/g,function(G){return Y[G]})}var Fw=/\/+/g;function ZH(A,Y){return typeof A==="object"&&A!==null&&A.key!=null?dU(""+A.key):Y.toString(36)}function mU(A){switch(A.status){case"fulfilled":return A.value;case"rejected":throw A.reason;default:switch(typeof A.status==="string"?A.then(WH,WH):(A.status="pending",A.then(function(Y){A.status==="pending"&&(A.status="fulfilled",A.value=Y)},function(Y){A.status==="pending"&&(A.status="rejected",A.reason=Y)})),A.status){case"fulfilled":return A.value;case"rejected":throw A.reason}}throw A}function QY(A,Y,G,H,X){var w=typeof A;if(w==="undefined"||w==="boolean")A=null;var Z=!1;if(A===null)Z=!0;else switch(w){case"bigint":case"string":case"number":Z=!0;break;case"object":switch(A.$$typeof){case qH:case CU:Z=!0;break;case Iw:return Z=A._init,QY(Z(A._payload),Y,G,H,X)}}if(Z)return X=X(A),Z=H===""?"."+ZH(A,0):H,Qw(X)?(G="",Z!=null&&(G=Z.replace(Fw,"$&/")+"/"),QY(X,Y,G,"",function(R){return R})):X!=null&&(RH(X)&&(X=uU(X,G+(X.key==null||A&&A.key===X.key?"":(""+X.key).replace(Fw,"$&/")+"/")+Z)),Y.push(X)),1;Z=0;var W=H===""?".":H+":";if(Qw(A))for(var q=0;q>>1,X=A[H];if(0>>1;HK4(W,G))qK4(R,W)?(A[H]=R,A[q]=G,H=q):(A[H]=W,A[Z]=G,H=Z);else if(qK4(R,G))A[H]=R,A[q]=G,H=q;else break A}}return Y}function K4(A,Y){var G=A.sortIndex-Y.sortIndex;return G!==0?G:A.id-Y.id}function S4(A){for(var Y=s0(KA);Y!==null;){if(Y.callback===null)j4(KA);else if(Y.startTime<=A)j4(KA),Y.sortIndex=Y.expirationTime,LH(YA,Y);else break;Y=s0(KA)}}function VH(A){if(L6=!1,S4(A),!B6)if(s0(YA)!==null)B6=!0,IY||(IY=!0,VY());else{var Y=s0(KA);Y!==null&&IH(VH,Y.startTime-A)}}function xw(){return FH?!0:r0()-$wA&&xw());){var H=x0.callback;if(typeof H==="function"){x0.callback=null,b4=x0.priorityLevel;var X=H(x0.expirationTime<=A);if(A=r0(),typeof X==="function"){x0.callback=X,S4(A),Y=!0;break Y}x0===s0(YA)&&j4(YA),S4(A)}else j4(YA);x0=s0(YA)}if(x0!==null)Y=!0;else{var w=s0(KA);w!==null&&IH(VH,w.startTime-A),Y=!1}}break A}finally{x0=null,b4=G,DH=!1}Y=void 0}}finally{Y?VY():IY=!1}}}function IH(A,Y){f6=jw(function(){A(r0())},Y)}var r0=void 0,fH,T4,zH,YA,KA,PM=1,x0=null,b4=3,DH=!1,B6=!1,L6=!1,FH=!1,jw,Cw,Nw,IY=!1,f6=-1,gM=5,$w=-1,VY,N4,QH,OH=5,KH=1,TH=4,OY=3,bH=2,SH=function(A){A.callback=null},NH=function(){return b4},jH=function(){FH=!0},KY=function(A,Y,G){var H=r0();switch(typeof G==="object"&&G!==null?(G=G.delay,G=typeof G==="number"&&0H?(A.sortIndex=G,LH(KA,A),s0(YA)===null&&A===s0(KA)&&(L6?(Cw(f6),f6=-1):L6=!0,IH(VH,G-H))):(A.sortIndex=X,LH(YA,A),B6||DH||(B6=!0,IY||(IY=!0,VY()))),A},CH;var kw=wH(()=>{if(typeof performance==="object"&&typeof performance.now==="function")fH=performance,r0=function(){return fH.now()};else T4=Date,zH=T4.now(),r0=function(){return T4.now()-zH};YA=[],KA=[],jw=typeof setTimeout==="function"?setTimeout:null,Cw=typeof clearTimeout==="function"?clearTimeout:null,Nw=typeof setImmediate<"u"?setImmediate:null;if(typeof Nw==="function")VY=function(){Nw(BH)};else if(typeof MessageChannel<"u")N4=new MessageChannel,QH=N4.port2,N4.port1.onmessage=BH,VY=function(){QH.postMessage(null)};else VY=function(){jw(BH,0)};CH=xw});var $H={};zw($H,{version:()=>rw,useFormStatus:()=>sw,useFormState:()=>pw,unstable_batchedUpdates:()=>iw,requestFormReset:()=>cw,preloadModule:()=>lw,preload:()=>mw,preinitModule:()=>dw,preinit:()=>uw,prefetchDNS:()=>vw,preconnect:()=>yw,flushSync:()=>Ew,createPortal:()=>hw,__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE:()=>gw});function Pw(A){var Y="https://react.dev/errors/"+A;if(1{_w=m0(p0(),1);R0={d:{f:TA,r:function(){throw Error(Pw(522))},D:TA,C:TA,L:TA,m:TA,X:TA,S:TA,M:TA},p:0,findDOMNode:null},EM=Symbol.for("react.portal");z6=_w.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;gw=R0});var tw=XH((OR,nw)=>{ow();function aw(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=="function")return;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(aw)}catch(A){console.error(A)}}aw(),nw.exports=$H});var Gw={};zw(Gw,{version:()=>MU,hydrateRoot:()=>UU,createRoot:()=>qU});function F(A){var Y="https://react.dev/errors/"+A;if(1$Y||(A.current=L8[$Y],L8[$Y]=null,$Y--)}function l(A,Y){$Y++,L8[$Y]=A.current,A.current=Y}function WG(A,Y){switch(l(hA,Y),l(i6,A),l(t0,null),Y.nodeType){case 9:case 11:A=(A=Y.documentElement)?(A=A.namespaceURI)?wZ(A):0:0;break;default:if(A=Y.tagName,Y=Y.namespaceURI)Y=wZ(Y),A=rq(Y,A);else switch(A){case"svg":A=1;break;case"math":A=2;break;default:A=0}}Z0(t0),l(t0,A)}function rY(){Z0(t0),Z0(i6),Z0(hA)}function f8(A){A.memoizedState!==null&&l(ZG,A);var Y=t0.current,G=rq(Y,A.type);Y!==G&&(l(i6,A),l(t0,G))}function qG(A){i6.current===A&&(Z0(t0),Z0(i6)),ZG.current===A&&(Z0(ZG),G4._currentValue=HY)}function tA(A){if(xH===void 0)try{throw Error()}catch(G){var Y=G.stack.trim().match(/\n( *(at )?)/);xH=Y&&Y[1]||"",Y9=-1)":-1X||q[H]!==R[X]){var z=` -`+q[H].replace(" at new "," at ");return A.displayName&&z.includes("")&&(z=z.replace("",A.displayName)),z}while(1<=H&&0<=X);break}}}finally{kH=!1,Error.prepareStackTrace=G}return(G=A?A.displayName||A.name:"")?tA(G):""}function lM(A,Y){switch(A.tag){case 26:case 27:case 5:return tA(A.type);case 16:return tA("Lazy");case 13:return A.child!==Y&&Y!==null?tA("Suspense Fallback"):tA("Suspense");case 19:return tA("SuspenseList");case 0:case 15:return _H(A.type,!1);case 11:return _H(A.type.render,!1);case 1:return _H(A.type,!0);case 31:return tA("Activity");default:return""}}function G9(A){try{var Y="",G=null;do Y+=lM(A,G),G=A,A=A.return;while(A);return Y}catch(H){return` -Error generating stack: `+H.message+` -`+H.stack}}function xA(A){if(typeof rM==="function"&&oM(A),S0&&typeof S0.setStrictMode==="function")try{S0.setStrictMode(Z4,A)}catch(Y){}}function tM(A){return A>>>=0,A===0?32:31-(aM(A)/nM|0)|0}function eA(A){var Y=A&42;if(Y!==0)return Y;switch(A&-A){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:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return A&261888;case 262144:case 524288:case 1048576:case 2097152:return A&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return A&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return A}}function hG(A,Y,G){var H=A.pendingLanes;if(H===0)return 0;var X=0,w=A.suspendedLanes,Z=A.pingedLanes;A=A.warmLanes;var W=H&134217727;return W!==0?(H=W&~w,H!==0?X=eA(H):(Z&=W,Z!==0?X=eA(Z):G||(G=W&~A,G!==0&&(X=eA(G))))):(W=H&~w,W!==0?X=eA(W):Z!==0?X=eA(Z):G||(G=H&~A,G!==0&&(X=eA(G)))),X===0?0:Y!==0&&Y!==X&&(Y&w)===0&&(w=X&-X,G=Y&-Y,w>=G||w===32&&(G&4194048)!==0)?Y:X}function W4(A,Y){return(A.pendingLanes&~(A.suspendedLanes&~A.pingedLanes)&Y)===0}function eM(A,Y){switch(A){case 1:case 2:case 4:case 8:case 64:return Y+250;case 16:case 32: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 Y+5000;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function kZ(){var A=_4;return _4<<=1,(_4&62914560)===0&&(_4=4194304),A}function gH(A){for(var Y=[],G=0;31>G;G++)Y.push(A);return Y}function q4(A,Y){A.pendingLanes|=Y,Y!==268435456&&(A.suspendedLanes=0,A.pingedLanes=0,A.warmLanes=0)}function AJ(A,Y,G,H,X,w){var Z=A.pendingLanes;A.pendingLanes=G,A.suspendedLanes=0,A.pingedLanes=0,A.warmLanes=0,A.expiredLanes&=G,A.entangledLanes&=G,A.errorRecoveryDisabledLanes&=G,A.shellSuspendCounter=0;var{entanglements:W,expirationTimes:q,hiddenUpdates:R}=A;for(G=Z&~G;0"u")return null;try{return A.activeElement||A.body}catch(Y){return A.body}}function h0(A){return A.replace(ZJ,function(Y){return"\\"+Y.charCodeAt(0).toString(16)+" "})}function F8(A,Y,G,H,X,w,Z,W){if(A.name="",Z!=null&&typeof Z!=="function"&&typeof Z!=="symbol"&&typeof Z!=="boolean"?A.type=Z:A.removeAttribute("type"),Y!=null)if(Z==="number"){if(Y===0&&A.value===""||A.value!=Y)A.value=""+_0(Y)}else A.value!==""+_0(Y)&&(A.value=""+_0(Y));else Z!=="submit"&&Z!=="reset"||A.removeAttribute("value");Y!=null?V8(A,Z,_0(Y)):G!=null?V8(A,Z,_0(G)):H!=null&&A.removeAttribute("value"),X==null&&w!=null&&(A.defaultChecked=!!w),X!=null&&(A.checked=X&&typeof X!=="function"&&typeof X!=="symbol"),W!=null&&typeof W!=="function"&&typeof W!=="symbol"&&typeof W!=="boolean"?A.name=""+_0(W):A.removeAttribute("name")}function dZ(A,Y,G,H,X,w,Z,W){if(w!=null&&typeof w!=="function"&&typeof w!=="symbol"&&typeof w!=="boolean"&&(A.type=w),Y!=null||G!=null){if(!(w!=="submit"&&w!=="reset"||Y!==void 0&&Y!==null)){Q8(A);return}G=G!=null?""+_0(G):"",Y=Y!=null?""+_0(Y):G,W||Y===A.value||(A.value=Y),A.defaultValue=Y}H=H!=null?H:X,H=typeof H!=="function"&&typeof H!=="symbol"&&!!H,A.checked=W?A.checked:!!H,A.defaultChecked=!!H,Z!=null&&typeof Z!=="function"&&typeof Z!=="symbol"&&typeof Z!=="boolean"&&(A.name=Z),Q8(A)}function V8(A,Y,G){Y==="number"&&MG(A.ownerDocument)===A||A.defaultValue===""+G||(A.defaultValue=""+G)}function mY(A,Y,G,H){if(A=A.options,Y){Y={};for(var X=0;X=Y)return{node:G,offset:Y-A};A=H}A:{for(;G;){if(G.nextSibling){G=G.nextSibling;break A}G=G.parentNode}G=void 0}G=Q9(G)}}function eZ(A,Y){return A&&Y?A===Y?!0:A&&A.nodeType===3?!1:Y&&Y.nodeType===3?eZ(A,Y.parentNode):("contains"in A)?A.contains(Y):A.compareDocumentPosition?!!(A.compareDocumentPosition(Y)&16):!1:!1}function AW(A){A=A!=null&&A.ownerDocument!=null&&A.ownerDocument.defaultView!=null?A.ownerDocument.defaultView:window;for(var Y=MG(A.document);Y instanceof A.HTMLIFrameElement;){try{var G=typeof Y.contentWindow.location.href==="string"}catch(H){G=!1}if(G)A=Y.contentWindow;else break;Y=MG(A.document)}return Y}function DX(A){var Y=A&&A.nodeName&&A.nodeName.toLowerCase();return Y&&(Y==="input"&&(A.type==="text"||A.type==="search"||A.type==="tel"||A.type==="url"||A.type==="password")||Y==="textarea"||A.contentEditable==="true")}function V9(A,Y,G){var H=G.window===G?G.document:G.nodeType===9?G:G.ownerDocument;T8||PY==null||PY!==MG(H)||(H=PY,("selectionStart"in H)&&DX(H)?H={start:H.selectionStart,end:H.selectionEnd}:(H=(H.ownerDocument&&H.ownerDocument.defaultView||window).getSelection(),H={anchorNode:H.anchorNode,anchorOffset:H.anchorOffset,focusNode:H.focusNode,focusOffset:H.focusOffset}),P6&&r6(P6,H)||(P6=H,H=jG(K8,"onSelect"),0>=Z,X-=Z,o0=1<<32-N0(Y)+X|G<N?(x=V,V=null):x=V.sibling;var g=L(J,V,B[N],D);if(g===null){V===null&&(V=x);break}A&&V&&g.alternate===null&&Y(J,V),U=w(g,U,N),$===null?K=g:$.sibling=g,$=g,V=x}if(N===B.length)return G(J,V),_&&WA(J,N),K;if(V===null){for(;NN?(x=V,V=null):x=V.sibling;var OA=L(J,V,g.value,D);if(OA===null){V===null&&(V=x);break}A&&V&&OA.alternate===null&&Y(J,V),U=w(OA,U,N),$===null?K=OA:$.sibling=OA,$=OA,V=x}if(g.done)return G(J,V),_&&WA(J,N),K;if(V===null){for(;!g.done;N++,g=B.next())g=Q(J,g.value,D),g!==null&&(U=w(g,U,N),$===null?K=g:$.sibling=g,$=g);return _&&WA(J,N),K}for(V=H(V);!g.done;N++,g=B.next())g=f(V,J,N,g.value,D),g!==null&&(A&&g.alternate!==null&&V.delete(g.key===null?N:g.key),U=w(g,U,N),$===null?K=g:$.sibling=g,$=g);return A&&V.forEach(function(TU){return Y(J,TU)}),_&&WA(J,N),K}function P(J,U,B,D){if(typeof B==="object"&&B!==null&&B.type===CY&&B.key===null&&(B=B.props.children),typeof B==="object"&&B!==null){switch(B.$$typeof){case $4:A:{for(var K=B.key;U!==null;){if(U.key===K){if(K=B.type,K===CY){if(U.tag===7){G(J,U.sibling),D=X(U,B.props.children),D.return=J,J=D;break A}}else if(U.elementType===K||typeof K==="object"&&K!==null&&K.$$typeof===bA&&YY(K)===U.type){G(J,U.sibling),D=X(U,B.props),F6(D,B),D.return=J,J=D;break A}G(J,U);break}else Y(J,U);U=U.sibling}B.type===CY?(D=XY(B.props.children,J.mode,D,B.key),D.return=J,J=D):(D=o4(B.type,B.key,B.props,null,J.mode,D),F6(D,B),D.return=J,J=D)}return Z(J);case K6:A:{for(K=B.key;U!==null;){if(U.key===K)if(U.tag===4&&U.stateNode.containerInfo===B.containerInfo&&U.stateNode.implementation===B.implementation){G(J,U.sibling),D=X(U,B.children||[]),D.return=J,J=D;break A}else{G(J,U);break}else Y(J,U);U=U.sibling}D=mH(B,J.mode,D),D.return=J,J=D}return Z(J);case bA:return B=YY(B),P(J,U,B,D)}if(T6(B))return I(J,U,B,D);if(D6(B)){if(K=D6(B),typeof K!=="function")throw Error(F(150));return B=K.call(B),T(J,U,B,D)}if(typeof B.then==="function")return P(J,U,E4(B),D);if(B.$$typeof===UA)return P(J,U,h4(J,B),D);y4(J,B)}return typeof B==="string"&&B!==""||typeof B==="number"||typeof B==="bigint"?(B=""+B,U!==null&&U.tag===6?(G(J,U.sibling),D=X(U,B),D.return=J,J=D):(G(J,U),D=dH(B,J.mode,D),D.return=J,J=D),Z(J)):G(J,U)}return function(J,U,B,D){try{n6=0;var K=P(J,U,B,D);return iY=null,K}catch(V){if(V===q6||V===mG)throw V;var $=K0(29,V,null,J.mode);return $.lanes=D,$.return=J,$}finally{}}}function SX(A){A.updateQueue={baseState:A.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function x8(A,Y){A=A.updateQueue,Y.updateQueue===A&&(Y.updateQueue={baseState:A.baseState,firstBaseUpdate:A.firstBaseUpdate,lastBaseUpdate:A.lastBaseUpdate,shared:A.shared,callbacks:null})}function yA(A){return{lane:A,tag:0,payload:null,callback:null,next:null}}function vA(A,Y,G){var H=A.updateQueue;if(H===null)return null;if(H=H.shared,(h&2)!==0){var X=H.pending;return X===null?Y.next=Y:(Y.next=X.next,X.next=Y),H.pending=Y,Y=RG(A),WW(A,null,G),Y}return dG(A,H,Y,G),RG(A)}function h6(A,Y,G){if(Y=Y.updateQueue,Y!==null&&(Y=Y.shared,(G&4194048)!==0)){var H=Y.lanes;H&=A.pendingLanes,G|=H,Y.lanes=G,PZ(A,G)}}function cH(A,Y){var{updateQueue:G,alternate:H}=A;if(H!==null&&(H=H.updateQueue,G===H)){var X=null,w=null;if(G=G.firstBaseUpdate,G!==null){do{var Z={lane:G.lane,tag:G.tag,payload:G.payload,callback:null,next:null};w===null?X=w=Z:w=w.next=Z,G=G.next}while(G!==null);w===null?X=w=Y:w=w.next=Y}else X=w=Y;G={baseState:H.baseState,firstBaseUpdate:X,lastBaseUpdate:w,shared:H.shared,callbacks:H.callbacks},A.updateQueue=G;return}A=G.lastBaseUpdate,A===null?G.firstBaseUpdate=Y:A.next=Y,G.lastBaseUpdate=Y}function E6(){if(k8){var A=cY;if(A!==null)throw A}}function y6(A,Y,G,H){k8=!1;var X=A.updateQueue;SA=!1;var{firstBaseUpdate:w,lastBaseUpdate:Z}=X,W=X.shared.pending;if(W!==null){X.shared.pending=null;var q=W,R=q.next;q.next=null,Z===null?w=R:Z.next=R,Z=q;var z=A.alternate;z!==null&&(z=z.updateQueue,W=z.lastBaseUpdate,W!==Z&&(W===null?z.firstBaseUpdate=R:W.next=R,z.lastBaseUpdate=q))}if(w!==null){var Q=X.baseState;Z=0,z=R=q=null,W=w;do{var L=W.lane&-536870913,f=L!==W.lane;if(f?(k&L)===L:(H&L)===L){L!==0&&L===nY&&(k8=!0),z!==null&&(z=z.next={lane:0,tag:W.tag,payload:W.payload,callback:null,next:null});A:{var I=A,T=W;L=Y;var P=G;switch(T.tag){case 1:if(I=T.payload,typeof I==="function"){Q=I.call(P,Q,L);break A}Q=I;break A;case 3:I.flags=I.flags&-65537|128;case 0:if(I=T.payload,L=typeof I==="function"?I.call(P,Q,L):I,L===null||L===void 0)break A;Q=s({},Q,L);break A;case 2:SA=!0}}L=W.callback,L!==null&&(A.flags|=64,f&&(A.flags|=8192),f=X.callbacks,f===null?X.callbacks=[L]:f.push(L))}else f={lane:L,tag:W.tag,payload:W.payload,callback:W.callback,next:null},z===null?(R=z=f,q=Q):z=z.next=f,Z|=L;if(W=W.next,W===null)if(W=X.shared.pending,W===null)break;else f=W,W=f.next,f.next=null,X.lastBaseUpdate=f,X.shared.pending=null}while(1);z===null&&(q=Q),X.baseState=q,X.firstBaseUpdate=R,X.lastBaseUpdate=z,w===null&&(X.shared.lanes=0),rA|=Z,A.lanes=Z,A.memoizedState=Q}}function DW(A,Y){if(typeof A!=="function")throw Error(F(191,A));A.call(Y)}function QW(A,Y){var G=A.callbacks;if(G!==null)for(A.callbacks=null,A=0;Aw?w:8;var Z=b.T,W={};b.T=W,vX(A,!1,Y,G);try{var q=X(),R=b.S;if(R!==null&&R(W,q),q!==null&&typeof q==="object"&&typeof q.then==="function"){var z=A1(q,H);v6(A,Y,z,j0(A))}else v6(A,Y,H,j0(A))}catch(Q){v6(A,Y,{then:function(){},status:"rejected",reason:Q},j0())}finally{E.p=w,Z!==null&&W.types!==null&&(Z.types=W.types),b.T=Z}}function Z1(){}function E8(A,Y,G,H){if(A.tag!==5)throw Error(F(476));var X=lW(A).queue;mW(A,X,Y,HY,G===null?Z1:function(){return cW(A),G(H)})}function lW(A){var Y=A.memoizedState;if(Y!==null)return Y;Y={memoizedState:HY,baseState:HY,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:DA,lastRenderedState:HY},next:null};var G={};return Y.next={memoizedState:G,baseState:G,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:DA,lastRenderedState:G},next:null},A.memoizedState=Y,A=A.alternate,A!==null&&(A.memoizedState=Y),Y}function cW(A){var Y=lW(A);Y.next===null&&(Y=A.alternate.memoizedState),v6(A,Y.next.queue,{},j0())}function yX(){return M0(G4)}function iW(){return t().memoizedState}function pW(){return t().memoizedState}function W1(A){for(var Y=A.return;Y!==null;){switch(Y.tag){case 24:case 3:var G=j0();A=yA(G);var H=vA(Y,A,G);H!==null&&(Q0(H,Y,G),h6(H,Y,G)),Y={cache:KX()},A.payload=Y;return}Y=Y.return}}function q1(A,Y,G){var H=j0();G={lane:H,revertLane:0,gesture:null,action:G,hasEagerState:!1,eagerState:null,next:null},pG(A)?rW(Y,G):(G=FX(A,Y,G,H),G!==null&&(Q0(G,A,H),oW(G,Y,H)))}function sW(A,Y,G){var H=j0();v6(A,Y,G,H)}function v6(A,Y,G,H){var X={lane:H,revertLane:0,gesture:null,action:G,hasEagerState:!1,eagerState:null,next:null};if(pG(A))rW(Y,X);else{var w=A.alternate;if(A.lanes===0&&(w===null||w.lanes===0)&&(w=Y.lastRenderedReducer,w!==null))try{var Z=Y.lastRenderedState,W=w(Z,G);if(X.hasEagerState=!0,X.eagerState=W,C0(W,Z))return dG(A,Y,X,0),m===null&&uG(),!1}catch(q){}finally{}if(G=FX(A,Y,X,H),G!==null)return Q0(G,A,H),oW(G,Y,H),!0}return!1}function vX(A,Y,G,H){if(H={lane:2,revertLane:rX(),gesture:null,action:H,hasEagerState:!1,eagerState:null,next:null},pG(A)){if(Y)throw Error(F(479))}else Y=FX(A,G,H,2),Y!==null&&Q0(Y,A,2)}function pG(A){var Y=A.alternate;return A===S||Y!==null&&Y===S}function rW(A,Y){pY=QG=!0;var G=A.pending;G===null?Y.next=Y:(Y.next=G.next,G.next=Y),A.pending=Y}function oW(A,Y,G){if((G&4194048)!==0){var H=Y.lanes;H&=A.pendingLanes,G|=H,Y.lanes=G,PZ(A,G)}}function pH(A,Y,G,H){Y=A.memoizedState,G=G(H,Y),G=G===null||G===void 0?Y:s({},Y,G),A.memoizedState=G,A.lanes===0&&(A.updateQueue.baseState=G)}function h9(A,Y,G,H,X,w,Z){return A=A.stateNode,typeof A.shouldComponentUpdate==="function"?A.shouldComponentUpdate(H,w,Z):Y.prototype&&Y.prototype.isPureReactComponent?!r6(G,H)||!r6(X,w):!0}function E9(A,Y,G,H){A=Y.state,typeof Y.componentWillReceiveProps==="function"&&Y.componentWillReceiveProps(G,H),typeof Y.UNSAFE_componentWillReceiveProps==="function"&&Y.UNSAFE_componentWillReceiveProps(G,H),Y.state!==A&&y8.enqueueReplaceState(Y,Y.state,null)}function RY(A,Y){var G=Y;if("ref"in Y){G={};for(var H in Y)H!=="ref"&&(G[H]=Y[H])}if(A=A.defaultProps){G===Y&&(G=s({},G));for(var X in A)G[X]===void 0&&(G[X]=A[X])}return G}function tW(A){JG(A)}function eW(A){console.error(A)}function Aq(A){JG(A)}function VG(A,Y){try{var G=A.onUncaughtError;G(Y.value,{componentStack:Y.stack})}catch(H){setTimeout(function(){throw H})}}function y9(A,Y,G){try{var H=A.onCaughtError;H(G.value,{componentStack:G.stack,errorBoundary:Y.tag===1?Y.stateNode:null})}catch(X){setTimeout(function(){throw X})}}function v8(A,Y,G){return G=yA(G),G.tag=3,G.payload={element:null},G.callback=function(){VG(A,Y)},G}function Yq(A){return A=yA(A),A.tag=3,A}function Gq(A,Y,G,H){var X=G.type.getDerivedStateFromError;if(typeof X==="function"){var w=H.value;A.payload=function(){return X(w)},A.callback=function(){y9(Y,G,H)}}var Z=G.stateNode;Z!==null&&typeof Z.componentDidCatch==="function"&&(A.callback=function(){y9(Y,G,H),typeof X!=="function"&&(uA===null?uA=new Set([this]):uA.add(this));var W=H.stack;this.componentDidCatch(H.value,{componentStack:W!==null?W:""})})}function U1(A,Y,G,H,X){if(G.flags|=32768,H!==null&&typeof H==="object"&&typeof H.then==="function"){if(Y=G.alternate,Y!==null&&W6(Y,G,X,!0),G=$0.current,G!==null){switch(G.tag){case 31:case 13:return v0===null?bG():G.alternate===null&&a===0&&(a=3),G.flags&=-257,G.flags|=65536,G.lanes=X,H===fG?G.flags|=16384:(Y=G.updateQueue,Y===null?G.updateQueue=new Set([H]):Y.add(H),H8(A,H,X)),!1;case 22:return G.flags|=65536,H===fG?G.flags|=16384:(Y=G.updateQueue,Y===null?(Y={transitions:null,markerInstances:null,retryQueue:new Set([H])},G.updateQueue=Y):(G=Y.retryQueue,G===null?Y.retryQueue=new Set([H]):G.add(H)),H8(A,H,X)),!1}throw Error(F(435,G.tag))}return H8(A,H,X),bG(),!1}if(_)return Y=$0.current,Y!==null?((Y.flags&65536)===0&&(Y.flags|=256),Y.flags|=65536,Y.lanes=X,H!==S8&&(A=Error(F(422),{cause:H}),a6(E0(A,G)))):(H!==S8&&(Y=Error(F(423),{cause:H}),a6(E0(Y,G))),A=A.current.alternate,A.flags|=65536,X&=-X,A.lanes|=X,H=E0(H,G),X=v8(A.stateNode,H,X),cH(A,X),a!==4&&(a=2)),!1;var w=Error(F(520),{cause:H});if(w=E0(w,G),m6===null?m6=[w]:m6.push(w),a!==4&&(a=2),Y===null)return!0;H=E0(H,G),G=Y;do{switch(G.tag){case 3:return G.flags|=65536,A=X&-X,G.lanes|=A,A=v8(G.stateNode,H,A),cH(G,A),!1;case 1:if(Y=G.type,w=G.stateNode,(G.flags&128)===0&&(typeof Y.getDerivedStateFromError==="function"||w!==null&&typeof w.componentDidCatch==="function"&&(uA===null||!uA.has(w))))return G.flags|=65536,X&=-X,G.lanes|=X,X=Yq(X),Gq(X,A,G,H),cH(G,X),!1}G=G.return}while(G!==null);return!1}function W0(A,Y,G,H){Y.child=A===null?zW(Y,null,G,H):MY(Y,A.child,G,H)}function v9(A,Y,G,H,X){G=G.render;var w=Y.ref;if("ref"in H){var Z={};for(var W in H)W!=="ref"&&(Z[W]=H[W])}else Z=H;if(UY(Y),H=CX(A,Y,G,Z,w,X),W=$X(),A!==null&&!G0)return xX(A,Y,X),QA(A,Y,X);return _&&W&&IX(Y),Y.flags|=1,W0(A,Y,H,X),Y.child}function u9(A,Y,G,H,X){if(A===null){var w=G.type;if(typeof w==="function"&&!VX(w)&&w.defaultProps===void 0&&G.compare===null)return Y.tag=15,Y.type=w,Hq(A,Y,w,H,X);return A=o4(G.type,null,H,Y,Y.mode,X),A.ref=Y.ref,A.return=Y,Y.child=A}if(w=A.child,!mX(A,X)){var Z=w.memoizedProps;if(G=G.compare,G=G!==null?G:r6,G(Z,H)&&A.ref===Y.ref)return QA(A,Y,X)}return Y.flags|=1,A=RA(w,H),A.ref=Y.ref,A.return=Y,Y.child=A}function Hq(A,Y,G,H,X){if(A!==null){var w=A.memoizedProps;if(r6(w,H)&&A.ref===Y.ref)if(G0=!1,Y.pendingProps=H=w,mX(A,X))(A.flags&131072)!==0&&(G0=!0);else return Y.lanes=A.lanes,QA(A,Y,X)}return u8(A,Y,G,H,X)}function Xq(A,Y,G,H){var X=H.children,w=A!==null?A.memoizedState:null;if(A===null&&Y.stateNode===null&&(Y.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),H.mode==="hidden"){if((Y.flags&128)!==0){if(w=w!==null?w.baseLanes|G:G,A!==null){H=Y.child=A.child;for(X=0;H!==null;)X=X|H.lanes|H.childLanes,H=H.sibling;H=X&~w}else H=0,Y.child=null;return d9(A,Y,w,G,H)}if((G&536870912)!==0)Y.memoizedState={baseLanes:0,cachePool:null},A!==null&&a4(Y,w!==null?w.cachePool:null),w!==null?C9(Y,w):_8(),FW(Y);else return H=Y.lanes=536870912,d9(A,Y,w!==null?w.baseLanes|G:G,G,H)}else w!==null?(a4(Y,w.cachePool),C9(Y,w),CA(Y),Y.memoizedState=null):(A!==null&&a4(Y,null),_8(),CA(Y));return W0(A,Y,X,G),Y.child}function j6(A,Y){return A!==null&&A.tag===22||Y.stateNode!==null||(Y.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),Y.sibling}function d9(A,Y,G,H,X){var w=TX();return w=w===null?null:{parent:Y0._currentValue,pool:w},Y.memoizedState={baseLanes:G,cachePool:w},A!==null&&a4(Y,null),_8(),FW(Y),A!==null&&W6(A,Y,H,!0),Y.childLanes=X,null}function e4(A,Y){return Y=IG({mode:Y.mode,children:Y.children},A.mode),Y.ref=A.ref,A.child=Y,Y.return=A,Y}function m9(A,Y,G){return MY(Y,A.child,null,G),A=e4(Y,Y.pendingProps),A.flags|=2,O0(Y),Y.memoizedState=null,A}function M1(A,Y,G){var H=Y.pendingProps,X=(Y.flags&128)!==0;if(Y.flags&=-129,A===null){if(_){if(H.mode==="hidden")return A=e4(Y,H),Y.lanes=536870912,j6(null,A);if(P8(Y),(A=p)?(A=aq(A,y0),A=A!==null&&A.data==="&"?A:null,A!==null&&(Y.memoizedState={dehydrated:A,treeContext:iA!==null?{id:o0,overflow:a0}:null,retryLane:536870912,hydrationErrors:null},G=UW(A),G.return=Y,Y.child=G,U0=Y,p=null)):A=null,A===null)throw pA(Y);return Y.lanes=536870912,null}return e4(Y,H)}var w=A.memoizedState;if(w!==null){var Z=w.dehydrated;if(P8(Y),X)if(Y.flags&256)Y.flags&=-257,Y=m9(A,Y,G);else if(Y.memoizedState!==null)Y.child=A.child,Y.flags|=128,Y=null;else throw Error(F(558));else if(G0||W6(A,Y,G,!1),X=(G&A.childLanes)!==0,G0||X){if(H=m,H!==null&&(Z=gZ(H,G),Z!==0&&Z!==w.retryLane))throw w.retryLane=Z,zY(A,Z),Q0(H,A,Z),dX;bG(),Y=m9(A,Y,G)}else A=w.treeContext,p=u0(Z.nextSibling),U0=Y,_=!0,EA=null,y0=!1,A!==null&&JW(Y,A),Y=e4(Y,H),Y.flags|=4096;return Y}return A=RA(A.child,{mode:H.mode,children:H.children}),A.ref=Y.ref,Y.child=A,A.return=Y,A}function AG(A,Y){var G=Y.ref;if(G===null)A!==null&&A.ref!==null&&(Y.flags|=4194816);else{if(typeof G!=="function"&&typeof G!=="object")throw Error(F(284));if(A===null||A.ref!==G)Y.flags|=4194816}}function u8(A,Y,G,H,X){if(UY(Y),G=CX(A,Y,G,H,void 0,X),H=$X(),A!==null&&!G0)return xX(A,Y,X),QA(A,Y,X);return _&&H&&IX(Y),Y.flags|=1,W0(A,Y,G,X),Y.child}function l9(A,Y,G,H,X,w){if(UY(Y),Y.updateQueue=null,G=IW(Y,H,G,X),VW(A),H=$X(),A!==null&&!G0)return xX(A,Y,w),QA(A,Y,w);return _&&H&&IX(Y),Y.flags|=1,W0(A,Y,G,w),Y.child}function c9(A,Y,G,H,X){if(UY(Y),Y.stateNode===null){var w=EY,Z=G.contextType;typeof Z==="object"&&Z!==null&&(w=M0(Z)),w=new G(H,w),Y.memoizedState=w.state!==null&&w.state!==void 0?w.state:null,w.updater=y8,Y.stateNode=w,w._reactInternals=Y,w=Y.stateNode,w.props=H,w.state=Y.memoizedState,w.refs={},SX(Y),Z=G.contextType,w.context=typeof Z==="object"&&Z!==null?M0(Z):EY,w.state=Y.memoizedState,Z=G.getDerivedStateFromProps,typeof Z==="function"&&(pH(Y,G,Z,H),w.state=Y.memoizedState),typeof G.getDerivedStateFromProps==="function"||typeof w.getSnapshotBeforeUpdate==="function"||typeof w.UNSAFE_componentWillMount!=="function"&&typeof w.componentWillMount!=="function"||(Z=w.state,typeof w.componentWillMount==="function"&&w.componentWillMount(),typeof w.UNSAFE_componentWillMount==="function"&&w.UNSAFE_componentWillMount(),Z!==w.state&&y8.enqueueReplaceState(w,w.state,null),y6(Y,H,w,X),E6(),w.state=Y.memoizedState),typeof w.componentDidMount==="function"&&(Y.flags|=4194308),H=!0}else if(A===null){w=Y.stateNode;var W=Y.memoizedProps,q=RY(G,W);w.props=q;var R=w.context,z=G.contextType;Z=EY,typeof z==="object"&&z!==null&&(Z=M0(z));var Q=G.getDerivedStateFromProps;z=typeof Q==="function"||typeof w.getSnapshotBeforeUpdate==="function",W=Y.pendingProps!==W,z||typeof w.UNSAFE_componentWillReceiveProps!=="function"&&typeof w.componentWillReceiveProps!=="function"||(W||R!==Z)&&E9(Y,w,H,Z),SA=!1;var L=Y.memoizedState;w.state=L,y6(Y,H,w,X),E6(),R=Y.memoizedState,W||L!==R||SA?(typeof Q==="function"&&(pH(Y,G,Q,H),R=Y.memoizedState),(q=SA||h9(Y,G,q,H,L,R,Z))?(z||typeof w.UNSAFE_componentWillMount!=="function"&&typeof w.componentWillMount!=="function"||(typeof w.componentWillMount==="function"&&w.componentWillMount(),typeof w.UNSAFE_componentWillMount==="function"&&w.UNSAFE_componentWillMount()),typeof w.componentDidMount==="function"&&(Y.flags|=4194308)):(typeof w.componentDidMount==="function"&&(Y.flags|=4194308),Y.memoizedProps=H,Y.memoizedState=R),w.props=H,w.state=R,w.context=Z,H=q):(typeof w.componentDidMount==="function"&&(Y.flags|=4194308),H=!1)}else{w=Y.stateNode,x8(A,Y),Z=Y.memoizedProps,z=RY(G,Z),w.props=z,Q=Y.pendingProps,L=w.context,R=G.contextType,q=EY,typeof R==="object"&&R!==null&&(q=M0(R)),W=G.getDerivedStateFromProps,(R=typeof W==="function"||typeof w.getSnapshotBeforeUpdate==="function")||typeof w.UNSAFE_componentWillReceiveProps!=="function"&&typeof w.componentWillReceiveProps!=="function"||(Z!==Q||L!==q)&&E9(Y,w,H,q),SA=!1,L=Y.memoizedState,w.state=L,y6(Y,H,w,X),E6();var f=Y.memoizedState;Z!==Q||L!==f||SA||A!==null&&A.dependencies!==null&&LG(A.dependencies)?(typeof W==="function"&&(pH(Y,G,W,H),f=Y.memoizedState),(z=SA||h9(Y,G,z,H,L,f,q)||A!==null&&A.dependencies!==null&&LG(A.dependencies))?(R||typeof w.UNSAFE_componentWillUpdate!=="function"&&typeof w.componentWillUpdate!=="function"||(typeof w.componentWillUpdate==="function"&&w.componentWillUpdate(H,f,q),typeof w.UNSAFE_componentWillUpdate==="function"&&w.UNSAFE_componentWillUpdate(H,f,q)),typeof w.componentDidUpdate==="function"&&(Y.flags|=4),typeof w.getSnapshotBeforeUpdate==="function"&&(Y.flags|=1024)):(typeof w.componentDidUpdate!=="function"||Z===A.memoizedProps&&L===A.memoizedState||(Y.flags|=4),typeof w.getSnapshotBeforeUpdate!=="function"||Z===A.memoizedProps&&L===A.memoizedState||(Y.flags|=1024),Y.memoizedProps=H,Y.memoizedState=f),w.props=H,w.state=f,w.context=q,H=z):(typeof w.componentDidUpdate!=="function"||Z===A.memoizedProps&&L===A.memoizedState||(Y.flags|=4),typeof w.getSnapshotBeforeUpdate!=="function"||Z===A.memoizedProps&&L===A.memoizedState||(Y.flags|=1024),H=!1)}return w=H,AG(A,Y),H=(Y.flags&128)!==0,w||H?(w=Y.stateNode,G=H&&typeof G.getDerivedStateFromError!=="function"?null:w.render(),Y.flags|=1,A!==null&&H?(Y.child=MY(Y,A.child,null,X),Y.child=MY(Y,null,G,X)):W0(A,Y,G,X),Y.memoizedState=w.state,A=Y.child):A=QA(A,Y,X),A}function i9(A,Y,G,H){return qY(),Y.flags|=256,W0(A,Y,G,H),Y.child}function rH(A){return{baseLanes:A,cachePool:BW()}}function oH(A,Y,G){return A=A!==null?A.childLanes&~G:0,Y&&(A|=T0),A}function wq(A,Y,G){var H=Y.pendingProps,X=!1,w=(Y.flags&128)!==0,Z;if((Z=w)||(Z=A!==null&&A.memoizedState===null?!1:(n.current&2)!==0),Z&&(X=!0,Y.flags&=-129),Z=(Y.flags&32)!==0,Y.flags&=-33,A===null){if(_){if(X?jA(Y):CA(Y),(A=p)?(A=aq(A,y0),A=A!==null&&A.data!=="&"?A:null,A!==null&&(Y.memoizedState={dehydrated:A,treeContext:iA!==null?{id:o0,overflow:a0}:null,retryLane:536870912,hydrationErrors:null},G=UW(A),G.return=Y,Y.child=G,U0=Y,p=null)):A=null,A===null)throw pA(Y);return GX(A)?Y.lanes=32:Y.lanes=536870912,null}var W=H.children;if(H=H.fallback,X)return CA(Y),X=Y.mode,W=IG({mode:"hidden",children:W},X),H=XY(H,X,G,null),W.return=Y,H.return=Y,W.sibling=H,Y.child=W,H=Y.child,H.memoizedState=rH(G),H.childLanes=oH(A,Z,G),Y.memoizedState=sH,j6(null,H);return jA(Y),d8(Y,W)}var q=A.memoizedState;if(q!==null&&(W=q.dehydrated,W!==null)){if(w)Y.flags&256?(jA(Y),Y.flags&=-257,Y=aH(A,Y,G)):Y.memoizedState!==null?(CA(Y),Y.child=A.child,Y.flags|=128,Y=null):(CA(Y),W=H.fallback,X=Y.mode,H=IG({mode:"visible",children:H.children},X),W=XY(W,X,G,null),W.flags|=2,H.return=Y,W.return=Y,H.sibling=W,Y.child=H,MY(Y,A.child,null,G),H=Y.child,H.memoizedState=rH(G),H.childLanes=oH(A,Z,G),Y.memoizedState=sH,Y=j6(null,H));else if(jA(Y),GX(W)){if(Z=W.nextSibling&&W.nextSibling.dataset,Z)var R=Z.dgst;Z=R,H=Error(F(419)),H.stack="",H.digest=Z,a6({value:H,source:null,stack:null}),Y=aH(A,Y,G)}else if(G0||W6(A,Y,G,!1),Z=(G&A.childLanes)!==0,G0||Z){if(Z=m,Z!==null&&(H=gZ(Z,G),H!==0&&H!==q.retryLane))throw q.retryLane=H,zY(A,H),Q0(Z,A,H),dX;YX(W)||bG(),Y=aH(A,Y,G)}else YX(W)?(Y.flags|=192,Y.child=A.child,Y=null):(A=q.treeContext,p=u0(W.nextSibling),U0=Y,_=!0,EA=null,y0=!1,A!==null&&JW(Y,A),Y=d8(Y,H.children),Y.flags|=4096);return Y}if(X)return CA(Y),W=H.fallback,X=Y.mode,q=A.child,R=q.sibling,H=RA(q,{mode:"hidden",children:H.children}),H.subtreeFlags=q.subtreeFlags&65011712,R!==null?W=RA(R,W):(W=XY(W,X,G,null),W.flags|=2),W.return=Y,H.return=Y,H.sibling=W,Y.child=H,j6(null,H),H=Y.child,W=A.child.memoizedState,W===null?W=rH(G):(X=W.cachePool,X!==null?(q=Y0._currentValue,X=X.parent!==q?{parent:q,pool:q}:X):X=BW(),W={baseLanes:W.baseLanes|G,cachePool:X}),H.memoizedState=W,H.childLanes=oH(A,Z,G),Y.memoizedState=sH,j6(A.child,H);return jA(Y),G=A.child,A=G.sibling,G=RA(G,{mode:"visible",children:H.children}),G.return=Y,G.sibling=null,A!==null&&(Z=Y.deletions,Z===null?(Y.deletions=[A],Y.flags|=16):Z.push(A)),Y.child=G,Y.memoizedState=null,G}function d8(A,Y){return Y=IG({mode:"visible",children:Y},A.mode),Y.return=A,A.child=Y}function IG(A,Y){return A=K0(22,A,null,Y),A.lanes=0,A}function aH(A,Y,G){return MY(Y,A.child,null,G),A=d8(Y,Y.pendingProps.children),A.flags|=2,Y.memoizedState=null,A}function p9(A,Y,G){A.lanes|=Y;var H=A.alternate;H!==null&&(H.lanes|=Y),j8(A.return,Y,G)}function nH(A,Y,G,H,X,w){var Z=A.memoizedState;Z===null?A.memoizedState={isBackwards:Y,rendering:null,renderingStartTime:0,last:H,tail:G,tailMode:X,treeForkCount:w}:(Z.isBackwards=Y,Z.rendering=null,Z.renderingStartTime=0,Z.last=H,Z.tail=G,Z.tailMode=X,Z.treeForkCount=w)}function Zq(A,Y,G){var H=Y.pendingProps,X=H.revealOrder,w=H.tail;H=H.children;var Z=n.current,W=(Z&2)!==0;if(W?(Z=Z&1|2,Y.flags|=128):Z&=1,l(n,Z),W0(A,Y,H,G),H=_?o6:0,!W&&A!==null&&(A.flags&128)!==0)A:for(A=Y.child;A!==null;){if(A.tag===13)A.memoizedState!==null&&p9(A,G,Y);else if(A.tag===19)p9(A,G,Y);else if(A.child!==null){A.child.return=A,A=A.child;continue}if(A===Y)break A;for(;A.sibling===null;){if(A.return===null||A.return===Y)break A;A=A.return}A.sibling.return=A.return,A=A.sibling}switch(X){case"forwards":G=Y.child;for(X=null;G!==null;)A=G.alternate,A!==null&&DG(A)===null&&(X=G),G=G.sibling;G=X,G===null?(X=Y.child,Y.child=null):(X=G.sibling,G.sibling=null),nH(Y,!1,X,G,w,H);break;case"backwards":case"unstable_legacy-backwards":G=null,X=Y.child;for(Y.child=null;X!==null;){if(A=X.alternate,A!==null&&DG(A)===null){Y.child=X;break}A=X.sibling,X.sibling=G,G=X,X=A}nH(Y,!0,G,null,w,H);break;case"together":nH(Y,!1,null,null,void 0,H);break;default:Y.memoizedState=null}return Y.child}function QA(A,Y,G){if(A!==null&&(Y.dependencies=A.dependencies),rA|=Y.lanes,(G&Y.childLanes)===0)if(A!==null){if(W6(A,Y,G,!1),(G&Y.childLanes)===0)return null}else return null;if(A!==null&&Y.child!==A.child)throw Error(F(153));if(Y.child!==null){A=Y.child,G=RA(A,A.pendingProps),Y.child=G;for(G.return=Y;A.sibling!==null;)A=A.sibling,G=G.sibling=RA(A,A.pendingProps),G.return=Y;G.sibling=null}return Y.child}function mX(A,Y){if((A.lanes&Y)!==0)return!0;return A=A.dependencies,A!==null&&LG(A)?!0:!1}function J1(A,Y,G){switch(Y.tag){case 3:WG(Y,Y.stateNode.containerInfo),NA(Y,Y0,A.memoizedState.cache),qY();break;case 27:case 5:f8(Y);break;case 4:WG(Y,Y.stateNode.containerInfo);break;case 10:NA(Y,Y.type,Y.memoizedProps.value);break;case 31:if(Y.memoizedState!==null)return Y.flags|=128,P8(Y),null;break;case 13:var H=Y.memoizedState;if(H!==null){if(H.dehydrated!==null)return jA(Y),Y.flags|=128,null;if((G&Y.child.childLanes)!==0)return wq(A,Y,G);return jA(Y),A=QA(A,Y,G),A!==null?A.sibling:null}jA(Y);break;case 19:var X=(A.flags&128)!==0;if(H=(G&Y.childLanes)!==0,H||(W6(A,Y,G,!1),H=(G&Y.childLanes)!==0),X){if(H)return Zq(A,Y,G);Y.flags|=128}if(X=Y.memoizedState,X!==null&&(X.rendering=null,X.tail=null,X.lastEffect=null),l(n,n.current),H)break;else return null;case 22:return Y.lanes=0,Xq(A,Y,G,Y.pendingProps);case 24:NA(Y,Y0,A.memoizedState.cache)}return QA(A,Y,G)}function Wq(A,Y,G){if(A!==null)if(A.memoizedProps!==Y.pendingProps)G0=!0;else{if(!mX(A,G)&&(Y.flags&128)===0)return G0=!1,J1(A,Y,G);G0=(A.flags&131072)!==0?!0:!1}else G0=!1,_&&(Y.flags&1048576)!==0&&MW(Y,o6,Y.index);switch(Y.lanes=0,Y.tag){case 16:A:{var H=Y.pendingProps;if(A=YY(Y.elementType),Y.type=A,typeof A==="function")VX(A)?(H=RY(A,H),Y.tag=1,Y=c9(null,Y,A,H,G)):(Y.tag=0,Y=u8(null,Y,A,H,G));else{if(A!==void 0&&A!==null){var X=A.$$typeof;if(X===ZX){Y.tag=11,Y=v9(null,Y,A,H,G);break A}else if(X===WX){Y.tag=14,Y=u9(null,Y,A,H,G);break A}}throw Y=B8(A)||A,Error(F(306,Y,""))}}return Y;case 0:return u8(A,Y,Y.type,Y.pendingProps,G);case 1:return H=Y.type,X=RY(H,Y.pendingProps),c9(A,Y,H,X,G);case 3:A:{if(WG(Y,Y.stateNode.containerInfo),A===null)throw Error(F(387));H=Y.pendingProps;var w=Y.memoizedState;X=w.element,x8(A,Y),y6(Y,H,null,G);var Z=Y.memoizedState;if(H=Z.cache,NA(Y,Y0,H),H!==w.cache&&C8(Y,[Y0],G,!0),E6(),H=Z.element,w.isDehydrated)if(w={element:H,isDehydrated:!1,cache:Z.cache},Y.updateQueue.baseState=w,Y.memoizedState=w,Y.flags&256){Y=i9(A,Y,H,G);break A}else if(H!==X){X=E0(Error(F(424)),Y),a6(X),Y=i9(A,Y,H,G);break A}else{switch(A=Y.stateNode.containerInfo,A.nodeType){case 9:A=A.body;break;default:A=A.nodeName==="HTML"?A.ownerDocument.body:A}p=u0(A.firstChild),U0=Y,_=!0,EA=null,y0=!0,G=zW(Y,null,H,G);for(Y.child=G;G;)G.flags=G.flags&-3|4096,G=G.sibling}else{if(qY(),H===X){Y=QA(A,Y,G);break A}W0(A,Y,H,G)}Y=Y.child}return Y;case 26:return AG(A,Y),A===null?(G=RZ(Y.type,null,Y.pendingProps,null))?Y.memoizedState=G:_||(G=Y.type,A=Y.pendingProps,H=CG(hA.current).createElement(G),H[q0]=Y,H[F0]=A,J0(H,G,A),w0(H),Y.stateNode=H):Y.memoizedState=RZ(Y.type,A.memoizedProps,Y.pendingProps,A.memoizedState),null;case 27:return f8(Y),A===null&&_&&(H=Y.stateNode=nq(Y.type,Y.pendingProps,hA.current),U0=Y,y0=!0,X=p,aA(Y.type)?(HX=X,p=u0(H.firstChild)):p=X),W0(A,Y,Y.pendingProps.children,G),AG(A,Y),A===null&&(Y.flags|=4194304),Y.child;case 5:if(A===null&&_){if(X=H=p)H=v1(H,Y.type,Y.pendingProps,y0),H!==null?(Y.stateNode=H,U0=Y,p=u0(H.firstChild),y0=!1,X=!0):X=!1;X||pA(Y)}return f8(Y),X=Y.type,w=Y.pendingProps,Z=A!==null?A.memoizedProps:null,H=w.children,e8(X,w)?H=null:Z!==null&&e8(X,Z)&&(Y.flags|=32),Y.memoizedState!==null&&(X=CX(A,Y,G1,null,null,G),G4._currentValue=X),AG(A,Y),W0(A,Y,H,G),Y.child;case 6:if(A===null&&_){if(A=G=p)G=u1(G,Y.pendingProps,y0),G!==null?(Y.stateNode=G,U0=Y,p=null,A=!0):A=!1;A||pA(Y)}return null;case 13:return wq(A,Y,G);case 4:return WG(Y,Y.stateNode.containerInfo),H=Y.pendingProps,A===null?Y.child=MY(Y,null,H,G):W0(A,Y,H,G),Y.child;case 11:return v9(A,Y,Y.type,Y.pendingProps,G);case 7:return W0(A,Y,Y.pendingProps,G),Y.child;case 8:return W0(A,Y,Y.pendingProps.children,G),Y.child;case 12:return W0(A,Y,Y.pendingProps.children,G),Y.child;case 10:return H=Y.pendingProps,NA(Y,Y.type,H.value),W0(A,Y,H.children,G),Y.child;case 9:return X=Y.type._context,H=Y.pendingProps.children,UY(Y),X=M0(X),H=H(X),Y.flags|=1,W0(A,Y,H,G),Y.child;case 14:return u9(A,Y,Y.type,Y.pendingProps,G);case 15:return Hq(A,Y,Y.type,Y.pendingProps,G);case 19:return Zq(A,Y,G);case 31:return M1(A,Y,G);case 22:return Xq(A,Y,G,Y.pendingProps);case 24:return UY(Y),H=M0(Y0),A===null?(X=TX(),X===null&&(X=m,w=KX(),X.pooledCache=w,w.refCount++,w!==null&&(X.pooledCacheLanes|=G),X=w),Y.memoizedState={parent:H,cache:X},SX(Y),NA(Y,Y0,X)):((A.lanes&G)!==0&&(x8(A,Y),y6(Y,null,null,G),E6()),X=A.memoizedState,w=Y.memoizedState,X.parent!==H?(X={parent:H,cache:H},Y.memoizedState=X,Y.lanes===0&&(Y.memoizedState=Y.updateQueue.baseState=X),NA(Y,Y0,H)):(H=w.cache,NA(Y,Y0,H),H!==X.cache&&C8(Y,[Y0],G,!0))),W0(A,Y,Y.pendingProps.children,G),Y.child;case 29:throw Y.pendingProps}throw Error(F(156,Y.tag))}function HA(A){A.flags|=4}function tH(A,Y,G,H,X){if(Y=(A.mode&32)!==0)Y=!1;if(Y){if(A.flags|=16777216,(X&335544128)===X)if(A.stateNode.complete)A.flags|=8192;else if(xq())A.flags|=8192;else throw ZY=fG,bX}else A.flags&=-16777217}function s9(A,Y){if(Y.type!=="stylesheet"||(Y.state.loading&4)!==0)A.flags&=-16777217;else if(A.flags|=16777216,!AU(Y))if(xq())A.flags|=8192;else throw ZY=fG,bX}function v4(A,Y){Y!==null&&(A.flags|=4),A.flags&16384&&(Y=A.tag!==22?kZ():536870912,A.lanes|=Y,A6|=Y)}function V6(A,Y){if(!_)switch(A.tailMode){case"hidden":Y=A.tail;for(var G=null;Y!==null;)Y.alternate!==null&&(G=Y),Y=Y.sibling;G===null?A.tail=null:G.sibling=null;break;case"collapsed":G=A.tail;for(var H=null;G!==null;)G.alternate!==null&&(H=G),G=G.sibling;H===null?Y||A.tail===null?A.tail=null:A.tail.sibling=null:H.sibling=null}}function i(A){var Y=A.alternate!==null&&A.alternate.child===A.child,G=0,H=0;if(Y)for(var X=A.child;X!==null;)G|=X.lanes|X.childLanes,H|=X.subtreeFlags&65011712,H|=X.flags&65011712,X.return=A,X=X.sibling;else for(X=A.child;X!==null;)G|=X.lanes|X.childLanes,H|=X.subtreeFlags,H|=X.flags,X.return=A,X=X.sibling;return A.subtreeFlags|=H,A.childLanes=G,Y}function R1(A,Y,G){var H=Y.pendingProps;switch(OX(Y),Y.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return i(Y),null;case 1:return i(Y),null;case 3:if(G=Y.stateNode,H=null,A!==null&&(H=A.memoizedState.cache),Y.memoizedState.cache!==H&&(Y.flags|=2048),BA(Y0),rY(),G.pendingContext&&(G.context=G.pendingContext,G.pendingContext=null),A===null||A.child===null)TY(Y)?HA(Y):A===null||A.memoizedState.isDehydrated&&(Y.flags&256)===0||(Y.flags|=1024,lH());return i(Y),null;case 26:var{type:X,memoizedState:w}=Y;return A===null?(HA(Y),w!==null?(i(Y),s9(Y,w)):(i(Y),tH(Y,X,null,H,G))):w?w!==A.memoizedState?(HA(Y),i(Y),s9(Y,w)):(i(Y),Y.flags&=-16777217):(A=A.memoizedProps,A!==H&&HA(Y),i(Y),tH(Y,X,A,H,G)),null;case 27:if(qG(Y),G=hA.current,X=Y.type,A!==null&&Y.stateNode!=null)A.memoizedProps!==H&&HA(Y);else{if(!H){if(Y.stateNode===null)throw Error(F(166));return i(Y),null}A=t0.current,TY(Y)?O9(Y,A):(A=nq(X,H,G),Y.stateNode=A,HA(Y))}return i(Y),null;case 5:if(qG(Y),X=Y.type,A!==null&&Y.stateNode!=null)A.memoizedProps!==H&&HA(Y);else{if(!H){if(Y.stateNode===null)throw Error(F(166));return i(Y),null}if(w=t0.current,TY(Y))O9(Y,w);else{var Z=CG(hA.current);switch(w){case 1:w=Z.createElementNS("http://www.w3.org/2000/svg",X);break;case 2:w=Z.createElementNS("http://www.w3.org/1998/Math/MathML",X);break;default:switch(X){case"svg":w=Z.createElementNS("http://www.w3.org/2000/svg",X);break;case"math":w=Z.createElementNS("http://www.w3.org/1998/Math/MathML",X);break;case"script":w=Z.createElement("div"),w.innerHTML="",w=w.removeChild(w.firstChild);break;case"select":w=typeof H.is==="string"?Z.createElement("select",{is:H.is}):Z.createElement("select"),H.multiple?w.multiple=!0:H.size&&(w.size=H.size);break;default:w=typeof H.is==="string"?Z.createElement(X,{is:H.is}):Z.createElement(X)}}w[q0]=Y,w[F0]=H;A:for(Z=Y.child;Z!==null;){if(Z.tag===5||Z.tag===6)w.appendChild(Z.stateNode);else if(Z.tag!==4&&Z.tag!==27&&Z.child!==null){Z.child.return=Z,Z=Z.child;continue}if(Z===Y)break A;for(;Z.sibling===null;){if(Z.return===null||Z.return===Y)break A;Z=Z.return}Z.sibling.return=Z.return,Z=Z.sibling}Y.stateNode=w;A:switch(J0(w,X,H),X){case"button":case"input":case"select":case"textarea":H=!!H.autoFocus;break A;case"img":H=!0;break A;default:H=!1}H&&HA(Y)}}return i(Y),tH(Y,Y.type,A===null?null:A.memoizedProps,Y.pendingProps,G),null;case 6:if(A&&Y.stateNode!=null)A.memoizedProps!==H&&HA(Y);else{if(typeof H!=="string"&&Y.stateNode===null)throw Error(F(166));if(A=hA.current,TY(Y)){if(A=Y.stateNode,G=Y.memoizedProps,H=null,X=U0,X!==null)switch(X.tag){case 27:case 5:H=X.memoizedProps}A[q0]=Y,A=A.nodeValue===G||H!==null&&H.suppressHydrationWarning===!0||sq(A.nodeValue,G)?!0:!1,A||pA(Y,!0)}else A=CG(A).createTextNode(H),A[q0]=Y,Y.stateNode=A}return i(Y),null;case 31:if(G=Y.memoizedState,A===null||A.memoizedState!==null){if(H=TY(Y),G!==null){if(A===null){if(!H)throw Error(F(318));if(A=Y.memoizedState,A=A!==null?A.dehydrated:null,!A)throw Error(F(557));A[q0]=Y}else qY(),(Y.flags&128)===0&&(Y.memoizedState=null),Y.flags|=4;i(Y),A=!1}else G=lH(),A!==null&&A.memoizedState!==null&&(A.memoizedState.hydrationErrors=G),A=!0;if(!A){if(Y.flags&256)return O0(Y),Y;return O0(Y),null}if((Y.flags&128)!==0)throw Error(F(558))}return i(Y),null;case 13:if(H=Y.memoizedState,A===null||A.memoizedState!==null&&A.memoizedState.dehydrated!==null){if(X=TY(Y),H!==null&&H.dehydrated!==null){if(A===null){if(!X)throw Error(F(318));if(X=Y.memoizedState,X=X!==null?X.dehydrated:null,!X)throw Error(F(317));X[q0]=Y}else qY(),(Y.flags&128)===0&&(Y.memoizedState=null),Y.flags|=4;i(Y),X=!1}else X=lH(),A!==null&&A.memoizedState!==null&&(A.memoizedState.hydrationErrors=X),X=!0;if(!X){if(Y.flags&256)return O0(Y),Y;return O0(Y),null}}if(O0(Y),(Y.flags&128)!==0)return Y.lanes=G,Y;return G=H!==null,A=A!==null&&A.memoizedState!==null,G&&(H=Y.child,X=null,H.alternate!==null&&H.alternate.memoizedState!==null&&H.alternate.memoizedState.cachePool!==null&&(X=H.alternate.memoizedState.cachePool.pool),w=null,H.memoizedState!==null&&H.memoizedState.cachePool!==null&&(w=H.memoizedState.cachePool.pool),w!==X&&(H.flags|=2048)),G!==A&&G&&(Y.child.flags|=8192),v4(Y,Y.updateQueue),i(Y),null;case 4:return rY(),A===null&&oX(Y.stateNode.containerInfo),i(Y),null;case 10:return BA(Y.type),i(Y),null;case 19:if(Z0(n),H=Y.memoizedState,H===null)return i(Y),null;if(X=(Y.flags&128)!==0,w=H.rendering,w===null)if(X)V6(H,!1);else{if(a!==0||A!==null&&(A.flags&128)!==0)for(A=Y.child;A!==null;){if(w=DG(A),w!==null){Y.flags|=128,V6(H,!1),A=w.updateQueue,Y.updateQueue=A,v4(Y,A),Y.subtreeFlags=0,A=G;for(G=Y.child;G!==null;)qW(G,A),G=G.sibling;return l(n,n.current&1|2),_&&WA(Y,H.treeForkCount),Y.child}A=A.sibling}H.tail!==null&&b0()>KG&&(Y.flags|=128,X=!0,V6(H,!1),Y.lanes=4194304)}else{if(!X)if(A=DG(w),A!==null){if(Y.flags|=128,X=!0,A=A.updateQueue,Y.updateQueue=A,v4(Y,A),V6(H,!0),H.tail===null&&H.tailMode==="hidden"&&!w.alternate&&!_)return i(Y),null}else 2*b0()-H.renderingStartTime>KG&&G!==536870912&&(Y.flags|=128,X=!0,V6(H,!1),Y.lanes=4194304);H.isBackwards?(w.sibling=Y.child,Y.child=w):(A=H.last,A!==null?A.sibling=w:Y.child=w,H.last=w)}if(H.tail!==null)return A=H.tail,H.rendering=A,H.tail=A.sibling,H.renderingStartTime=b0(),A.sibling=null,G=n.current,l(n,X?G&1|2:G&1),_&&WA(Y,H.treeForkCount),A;return i(Y),null;case 22:case 23:return O0(Y),NX(),H=Y.memoizedState!==null,A!==null?A.memoizedState!==null!==H&&(Y.flags|=8192):H&&(Y.flags|=8192),H?(G&536870912)!==0&&(Y.flags&128)===0&&(i(Y),Y.subtreeFlags&6&&(Y.flags|=8192)):i(Y),G=Y.updateQueue,G!==null&&v4(Y,G.retryQueue),G=null,A!==null&&A.memoizedState!==null&&A.memoizedState.cachePool!==null&&(G=A.memoizedState.cachePool.pool),H=null,Y.memoizedState!==null&&Y.memoizedState.cachePool!==null&&(H=Y.memoizedState.cachePool.pool),H!==G&&(Y.flags|=2048),A!==null&&Z0(wY),null;case 24:return G=null,A!==null&&(G=A.memoizedState.cache),Y.memoizedState.cache!==G&&(Y.flags|=2048),BA(Y0),i(Y),null;case 25:return null;case 30:return null}throw Error(F(156,Y.tag))}function B1(A,Y){switch(OX(Y),Y.tag){case 1:return A=Y.flags,A&65536?(Y.flags=A&-65537|128,Y):null;case 3:return BA(Y0),rY(),A=Y.flags,(A&65536)!==0&&(A&128)===0?(Y.flags=A&-65537|128,Y):null;case 26:case 27:case 5:return qG(Y),null;case 31:if(Y.memoizedState!==null){if(O0(Y),Y.alternate===null)throw Error(F(340));qY()}return A=Y.flags,A&65536?(Y.flags=A&-65537|128,Y):null;case 13:if(O0(Y),A=Y.memoizedState,A!==null&&A.dehydrated!==null){if(Y.alternate===null)throw Error(F(340));qY()}return A=Y.flags,A&65536?(Y.flags=A&-65537|128,Y):null;case 19:return Z0(n),null;case 4:return rY(),null;case 10:return BA(Y.type),null;case 22:case 23:return O0(Y),NX(),A!==null&&Z0(wY),A=Y.flags,A&65536?(Y.flags=A&-65537|128,Y):null;case 24:return BA(Y0),null;case 25:return null;default:return null}}function qq(A,Y){switch(OX(Y),Y.tag){case 3:BA(Y0),rY();break;case 26:case 27:case 5:qG(Y);break;case 4:rY();break;case 31:Y.memoizedState!==null&&O0(Y);break;case 13:O0(Y);break;case 19:Z0(n);break;case 10:BA(Y.type);break;case 22:case 23:O0(Y),NX(),A!==null&&Z0(wY);break;case 24:BA(Y0)}}function B4(A,Y){try{var G=Y.updateQueue,H=G!==null?G.lastEffect:null;if(H!==null){var X=H.next;G=X;do{if((G.tag&A)===A){H=void 0;var{create:w,inst:Z}=G;H=w(),Z.destroy=H}G=G.next}while(G!==X)}}catch(W){v(Y,Y.return,W)}}function sA(A,Y,G){try{var H=Y.updateQueue,X=H!==null?H.lastEffect:null;if(X!==null){var w=X.next;H=w;do{if((H.tag&A)===A){var Z=H.inst,W=Z.destroy;if(W!==void 0){Z.destroy=void 0,X=Y;var q=G,R=W;try{R()}catch(z){v(X,q,z)}}}H=H.next}while(H!==w)}}catch(z){v(Y,Y.return,z)}}function Uq(A){var Y=A.updateQueue;if(Y!==null){var G=A.stateNode;try{QW(Y,G)}catch(H){v(A,A.return,H)}}}function Mq(A,Y,G){G.props=RY(A.type,A.memoizedProps),G.state=A.memoizedState;try{G.componentWillUnmount()}catch(H){v(A,Y,H)}}function u6(A,Y){try{var G=A.ref;if(G!==null){switch(A.tag){case 26:case 27:case 5:var H=A.stateNode;break;case 30:H=A.stateNode;break;default:H=A.stateNode}typeof G==="function"?A.refCleanup=G(H):G.current=H}}catch(X){v(A,Y,X)}}function n0(A,Y){var{ref:G,refCleanup:H}=A;if(G!==null)if(typeof H==="function")try{H()}catch(X){v(A,Y,X)}finally{A.refCleanup=null,A=A.alternate,A!=null&&(A.refCleanup=null)}else if(typeof G==="function")try{G(null)}catch(X){v(A,Y,X)}else G.current=null}function Jq(A){var{type:Y,memoizedProps:G,stateNode:H}=A;try{A:switch(Y){case"button":case"input":case"select":case"textarea":G.autoFocus&&H.focus();break A;case"img":G.src?H.src=G.src:G.srcSet&&(H.srcset=G.srcSet)}}catch(X){v(A,A.return,X)}}function eH(A,Y,G){try{var H=A.stateNode;_1(H,A.type,G,Y),H[F0]=Y}catch(X){v(A,A.return,X)}}function Rq(A){return A.tag===5||A.tag===3||A.tag===26||A.tag===27&&aA(A.type)||A.tag===4}function A8(A){A:for(;;){for(;A.sibling===null;){if(A.return===null||Rq(A.return))return null;A=A.return}A.sibling.return=A.return;for(A=A.sibling;A.tag!==5&&A.tag!==6&&A.tag!==18;){if(A.tag===27&&aA(A.type))continue A;if(A.flags&2)continue A;if(A.child===null||A.tag===4)continue A;else A.child.return=A,A=A.child}if(!(A.flags&2))return A.stateNode}}function m8(A,Y,G){var H=A.tag;if(H===5||H===6)A=A.stateNode,Y?(G.nodeType===9?G.body:G.nodeName==="HTML"?G.ownerDocument.body:G).insertBefore(A,Y):(Y=G.nodeType===9?G.body:G.nodeName==="HTML"?G.ownerDocument.body:G,Y.appendChild(A),G=G._reactRootContainer,G!==null&&G!==void 0||Y.onclick!==null||(Y.onclick=MA));else if(H!==4&&(H===27&&aA(A.type)&&(G=A.stateNode,Y=null),A=A.child,A!==null))for(m8(A,Y,G),A=A.sibling;A!==null;)m8(A,Y,G),A=A.sibling}function OG(A,Y,G){var H=A.tag;if(H===5||H===6)A=A.stateNode,Y?G.insertBefore(A,Y):G.appendChild(A);else if(H!==4&&(H===27&&aA(A.type)&&(G=A.stateNode),A=A.child,A!==null))for(OG(A,Y,G),A=A.sibling;A!==null;)OG(A,Y,G),A=A.sibling}function Bq(A){var{stateNode:Y,memoizedProps:G}=A;try{for(var H=A.type,X=Y.attributes;X.length;)Y.removeAttributeNode(X[0]);J0(Y,H,G),Y[q0]=A,Y[F0]=G}catch(w){v(A,A.return,w)}}function L1(A,Y){if(A=A.containerInfo,n8=_G,A=AW(A),DX(A)){if("selectionStart"in A)var G={start:A.selectionStart,end:A.selectionEnd};else A:{G=(G=A.ownerDocument)&&G.defaultView||window;var H=G.getSelection&&G.getSelection();if(H&&H.rangeCount!==0){G=H.anchorNode;var{anchorOffset:X,focusNode:w}=H;H=H.focusOffset;try{G.nodeType,w.nodeType}catch(T){G=null;break A}var Z=0,W=-1,q=-1,R=0,z=0,Q=A,L=null;Y:for(;;){for(var f;;){if(Q!==G||X!==0&&Q.nodeType!==3||(W=Z+X),Q!==w||H!==0&&Q.nodeType!==3||(q=Z+H),Q.nodeType===3&&(Z+=Q.nodeValue.length),(f=Q.firstChild)===null)break;L=Q,Q=f}for(;;){if(Q===A)break Y;if(L===G&&++R===X&&(W=Z),L===w&&++z===H&&(q=Z),(f=Q.nextSibling)!==null)break;Q=L,L=Q.parentNode}Q=f}G=W===-1||q===-1?null:{start:W,end:q}}else G=null}G=G||{start:0,end:0}}else G=null;t8={focusedElem:A,selectionRange:G},_G=!1;for(X0=Y;X0!==null;)if(Y=X0,A=Y.child,(Y.subtreeFlags&1028)!==0&&A!==null)A.return=Y,X0=A;else for(;X0!==null;){switch(Y=X0,w=Y.alternate,A=Y.flags,Y.tag){case 0:if((A&4)!==0&&(A=Y.updateQueue,A=A!==null?A.events:null,A!==null))for(G=0;G title"));J0(w,H,G),w[q0]=A,w0(w),H=w;break A;case"link":var Z=LZ("link","href",X).get(H+(G.href||""));if(Z){for(var W=0;WP&&(Z=P,P=T,T=Z);var J=F9(W,T),U=F9(W,P);if(J&&U&&(f.rangeCount!==1||f.anchorNode!==J.node||f.anchorOffset!==J.offset||f.focusNode!==U.node||f.focusOffset!==U.offset)){var B=Q.createRange();B.setStart(J.node,J.offset),f.removeAllRanges(),T>P?(f.addRange(B),f.extend(U.node,U.offset)):(B.setEnd(U.node,U.offset),f.addRange(B))}}}}Q=[];for(f=W;f=f.parentNode;)f.nodeType===1&&Q.push({element:f,left:f.scrollLeft,top:f.scrollTop});typeof W.focus==="function"&&W.focus();for(W=0;WG?32:G,b.T=null,G=i8,i8=null;var w=dA,Z=LA;if(H0=0,Y6=dA=null,LA=0,(h&6)!==0)throw Error(F(331));var W=h;if(h|=4,Tq(w.current),Iq(w,w.current,Z,G),h=W,L4(0,!1),S0&&typeof S0.onPostCommitFiberRoot==="function")try{S0.onPostCommitFiberRoot(Z4,w)}catch(q){}return!0}finally{E.p=X,b.T=H,vq(A,Y)}}function t9(A,Y,G){Y=E0(G,Y),Y=v8(A.stateNode,Y,2),A=vA(A,Y,2),A!==null&&(q4(A,2),AA(A))}function v(A,Y,G){if(A.tag===3)t9(A,A,G);else for(;Y!==null;){if(Y.tag===3){t9(Y,A,G);break}else if(Y.tag===1){var H=Y.stateNode;if(typeof Y.type.getDerivedStateFromError==="function"||typeof H.componentDidCatch==="function"&&(uA===null||!uA.has(H))){A=E0(G,A),G=Yq(2),H=vA(Y,G,2),H!==null&&(Gq(G,H,Y,A),q4(H,2),AA(H));break}}Y=Y.return}}function H8(A,Y,G){var H=A.pingCache;if(H===null){H=A.pingCache=new D1;var X=new Set;H.set(Y,X)}else X=H.get(Y),X===void 0&&(X=new Set,H.set(Y,X));X.has(G)||(iX=!0,X.add(G),A=O1.bind(null,A,Y,G),Y.then(A,A))}function O1(A,Y,G){var H=A.pingCache;H!==null&&H.delete(Y),A.pingedLanes|=A.suspendedLanes&G,A.warmLanes&=~G,m===A&&(k&G)===G&&(a===4||a===3&&(k&62914560)===k&&300>b0()-sG?(h&2)===0&&G6(A,0):pX|=G,A6===k&&(A6=0)),AA(A)}function dq(A,Y){Y===0&&(Y=kZ()),A=zY(A,Y),A!==null&&(q4(A,Y),AA(A))}function K1(A){var Y=A.memoizedState,G=0;Y!==null&&(G=Y.retryLane),dq(A,G)}function T1(A,Y){var G=0;switch(A.tag){case 31:case 13:var{stateNode:H,memoizedState:X}=A;X!==null&&(G=X.retryLane);break;case 19:H=A.stateNode;break;case 22:H=A.stateNode._retryCache;break;default:throw Error(F(314))}H!==null&&H.delete(Y),dq(A,G)}function b1(A,Y){return qX(A,Y)}function AA(A){A!==NY&&A.next===null&&(NY===null?SG=NY=A:NY=NY.next=A),NG=!0,s8||(s8=!0,N1())}function L4(A,Y){if(!X8&&NG){X8=!0;do{var G=!1;for(var H=SG;H!==null;){if(!Y)if(A!==0){var X=H.pendingLanes;if(X===0)var w=0;else{var{suspendedLanes:Z,pingedLanes:W}=H;w=(1<<31-N0(42|A)+1)-1,w&=X&~(Z&~W),w=w&201326741?w&201326741|1:w?w|2:0}w!==0&&(G=!0,e9(H,w))}else w=k,w=hG(H,H===m?w:0,H.cancelPendingCommit!==null||H.timeoutHandle!==-1),(w&3)===0||W4(H,w)||(G=!0,e9(H,w));H=H.next}}while(G);X8=!1}}function S1(){mq()}function mq(){NG=s8=!1;var A=0;gA!==0&&g1()&&(A=gA);for(var Y=b0(),G=null,H=SG;H!==null;){var X=H.next,w=lq(H,Y);if(w===0)H.next=null,G===null?SG=X:G.next=X,X===null&&(NY=G);else if(G=H,A!==0||(w&3)!==0)NG=!0;H=X}H0!==0&&H0!==5||L4(A,!1),gA!==0&&(gA=0)}function lq(A,Y){for(var{suspendedLanes:G,pingedLanes:H,expirationTimes:X}=A,w=A.pendingLanes&-62914561;0W)break;var{transferSize:z,initiatorType:Q}=q;z&&XZ(Q)&&(q=q.responseEnd,Z+=z*(q title"):null)}function t1(A,Y,G){if(G===1||Y.itemProp!=null)return!1;switch(A){case"meta":case"title":return!0;case"style":if(typeof Y.precedence!=="string"||typeof Y.href!=="string"||Y.href==="")break;return!0;case"link":if(typeof Y.rel!=="string"||typeof Y.href!=="string"||Y.href===""||Y.onLoad||Y.onError)break;switch(Y.rel){case"stylesheet":return A=Y.disabled,typeof Y.precedence==="string"&&A==null;default:return!0}case"script":if(Y.async&&typeof Y.async!=="function"&&typeof Y.async!=="symbol"&&!Y.onLoad&&!Y.onError&&Y.src&&typeof Y.src==="string")return!0}return!1}function AU(A){return A.type==="stylesheet"&&(A.state.loading&3)===0?!1:!0}function e1(A,Y,G,H){if(G.type==="stylesheet"&&(typeof H.media!=="string"||matchMedia(H.media).matches!==!1)&&(G.state.loading&4)===0){if(G.instance===null){var X=H6(H.href),w=Y.querySelector(f4(X));if(w){Y=w._p,Y!==null&&typeof Y==="object"&&typeof Y.then==="function"&&(A.count++,A=xG.bind(A),Y.then(A,A)),G.state.loading|=4,G.instance=w,w0(w);return}w=Y.ownerDocument||Y,H=eq(H),(X=d0.get(X))&&aX(H,X),w=w.createElement("link"),w0(w);var Z=w;Z._p=new Promise(function(W,q){Z.onload=W,Z.onerror=q}),J0(w,"link",H),G.instance=w}A.stylesheets===null&&(A.stylesheets=new Map),A.stylesheets.set(G,Y),(Y=G.state.preload)&&(G.state.loading&3)===0&&(A.count++,G=xG.bind(A),Y.addEventListener("load",G),Y.addEventListener("error",G))}}function AR(A,Y){return A.stylesheets&&A.count===0&&XG(A,A.stylesheets),0q8?50:800)+Y);return A.unsuspend=G,function(){A.unsuspend=null,clearTimeout(H),clearTimeout(X)}}:null}function xG(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)XG(this,this.stylesheets);else if(this.unsuspend){var A=this.unsuspend;this.unsuspend=null,A()}}}function XG(A,Y){A.stylesheets=null,A.unsuspend!==null&&(A.count++,kG=new Map,Y.forEach(YR,A),kG=null,xG.call(A))}function YR(A,Y){if(!(Y.state.loading&4)){var G=kG.get(A);if(G)var H=G.get(null);else{G=new Map,kG.set(A,G);for(var X=A.querySelectorAll("link[data-precedence],style[data-precedence]"),w=0;w{kw();gG=m0(p0(),1),OZ=m0(tw(),1);s=Object.assign,uM=Symbol.for("react.element"),$4=Symbol.for("react.transitional.element"),K6=Symbol.for("react.portal"),CY=Symbol.for("react.fragment"),NZ=Symbol.for("react.strict_mode"),U8=Symbol.for("react.profiler"),jZ=Symbol.for("react.consumer"),UA=Symbol.for("react.context"),ZX=Symbol.for("react.forward_ref"),M8=Symbol.for("react.suspense"),J8=Symbol.for("react.suspense_list"),WX=Symbol.for("react.memo"),bA=Symbol.for("react.lazy"),R8=Symbol.for("react.activity"),dM=Symbol.for("react.memo_cache_sentinel"),A9=Symbol.iterator;mM=Symbol.for("react.client.reference");T6=Array.isArray,b=gG.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,E=OZ.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,HY={pending:!1,data:null,method:null,action:null},L8=[];t0=e0(null),i6=e0(null),hA=e0(null),ZG=e0(null);z8=Object.prototype.hasOwnProperty,qX=KY,PH=SH,cM=CH,iM=jH,b0=r0,pM=NH,CZ=KH,$Z=bH,UG=OY,sM=TH,xZ=OH,rM=void 0,oM=void 0;N0=Math.clz32?Math.clz32:tM,aM=Math.log,nM=Math.LN2;oA=Math.random().toString(36).slice(2),q0="__reactFiber$"+oA,F0="__reactProps$"+oA,w6="__reactContainer$"+oA,D8="__reactEvents$"+oA,YJ="__reactListeners$"+oA,GJ="__reactHandles$"+oA,X9="__reactResources$"+oA,U4="__reactMarker$"+oA;EZ=new Set,yZ={};HJ=RegExp("^[: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]*$"),w9={},Z9={};ZJ=/[\n"\\]/g;WJ=new Set("animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp".split(" "));qJ=new Map([["acceptCharset","accept-charset"],["htmlFor","for"],["httpEquiv","http-equiv"],["crossOrigin","crossorigin"],["accentHeight","accent-height"],["alignmentBaseline","alignment-baseline"],["arabicForm","arabic-form"],["baselineShift","baseline-shift"],["capHeight","cap-height"],["clipPath","clip-path"],["clipRule","clip-rule"],["colorInterpolation","color-interpolation"],["colorInterpolationFilters","color-interpolation-filters"],["colorProfile","color-profile"],["colorRendering","color-rendering"],["dominantBaseline","dominant-baseline"],["enableBackground","enable-background"],["fillOpacity","fill-opacity"],["fillRule","fill-rule"],["floodColor","flood-color"],["floodOpacity","flood-opacity"],["fontFamily","font-family"],["fontSize","font-size"],["fontSizeAdjust","font-size-adjust"],["fontStretch","font-stretch"],["fontStyle","font-style"],["fontVariant","font-variant"],["fontWeight","font-weight"],["glyphName","glyph-name"],["glyphOrientationHorizontal","glyph-orientation-horizontal"],["glyphOrientationVertical","glyph-orientation-vertical"],["horizAdvX","horiz-adv-x"],["horizOriginX","horiz-origin-x"],["imageRendering","image-rendering"],["letterSpacing","letter-spacing"],["lightingColor","lighting-color"],["markerEnd","marker-end"],["markerMid","marker-mid"],["markerStart","marker-start"],["overlinePosition","overline-position"],["overlineThickness","overline-thickness"],["paintOrder","paint-order"],["panose-1","panose-1"],["pointerEvents","pointer-events"],["renderingIntent","rendering-intent"],["shapeRendering","shape-rendering"],["stopColor","stop-color"],["stopOpacity","stop-opacity"],["strikethroughPosition","strikethrough-position"],["strikethroughThickness","strikethrough-thickness"],["strokeDasharray","stroke-dasharray"],["strokeDashoffset","stroke-dashoffset"],["strokeLinecap","stroke-linecap"],["strokeLinejoin","stroke-linejoin"],["strokeMiterlimit","stroke-miterlimit"],["strokeOpacity","stroke-opacity"],["strokeWidth","stroke-width"],["textAnchor","text-anchor"],["textDecoration","text-decoration"],["textRendering","text-rendering"],["transformOrigin","transform-origin"],["underlinePosition","underline-position"],["underlineThickness","underline-thickness"],["unicodeBidi","unicode-bidi"],["unicodeRange","unicode-range"],["unitsPerEm","units-per-em"],["vAlphabetic","v-alphabetic"],["vHanging","v-hanging"],["vIdeographic","v-ideographic"],["vMathematical","v-mathematical"],["vectorEffect","vector-effect"],["vertAdvY","vert-adv-y"],["vertOriginX","vert-origin-x"],["vertOriginY","vert-origin-y"],["wordSpacing","word-spacing"],["writingMode","writing-mode"],["xmlnsXlink","xmlns:xlink"],["xHeight","x-height"]]),UJ=/^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;fA=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u");if(fA)try{AY={},Object.defineProperty(AY,"passive",{get:function(){O8=!0}}),window.addEventListener("test",AY,AY),window.removeEventListener("test",AY,AY)}catch(A){O8=!1}LY={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(A){return A.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},EG=V0(LY),M4=s({},LY,{view:0,detail:0}),MJ=V0(M4),yG=s({},M4,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:fX,button:0,buttons:0,relatedTarget:function(A){return A.relatedTarget===void 0?A.fromElement===A.srcElement?A.toElement:A.fromElement:A.relatedTarget},movementX:function(A){if("movementX"in A)return A.movementX;return A!==Q6&&(Q6&&A.type==="mousemove"?(EH=A.screenX-Q6.screenX,yH=A.screenY-Q6.screenY):yH=EH=0,Q6=A),EH},movementY:function(A){return"movementY"in A?A.movementY:yH}}),M9=V0(yG),JJ=s({},yG,{dataTransfer:0}),RJ=V0(JJ),BJ=s({},M4,{relatedTarget:0}),vH=V0(BJ),LJ=s({},LY,{animationName:0,elapsedTime:0,pseudoElement:0}),fJ=V0(LJ),zJ=s({},LY,{clipboardData:function(A){return"clipboardData"in A?A.clipboardData:window.clipboardData}}),DJ=V0(zJ),QJ=s({},LY,{data:0}),J9=V0(QJ),FJ={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},VJ={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},IJ={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};KJ=s({},M4,{key:function(A){if(A.key){var Y=FJ[A.key]||A.key;if(Y!=="Unidentified")return Y}return A.type==="keypress"?(A=s4(A),A===13?"Enter":String.fromCharCode(A)):A.type==="keydown"||A.type==="keyup"?VJ[A.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:fX,charCode:function(A){return A.type==="keypress"?s4(A):0},keyCode:function(A){return A.type==="keydown"||A.type==="keyup"?A.keyCode:0},which:function(A){return A.type==="keypress"?s4(A):A.type==="keydown"||A.type==="keyup"?A.keyCode:0}}),TJ=V0(KJ),bJ=s({},yG,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),R9=V0(bJ),SJ=s({},M4,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:fX}),NJ=V0(SJ),jJ=s({},LY,{propertyName:0,elapsedTime:0,pseudoElement:0}),CJ=V0(jJ),$J=s({},yG,{deltaX:function(A){return"deltaX"in A?A.deltaX:("wheelDeltaX"in A)?-A.wheelDeltaX:0},deltaY:function(A){return"deltaY"in A?A.deltaY:("wheelDeltaY"in A)?-A.wheelDeltaY:("wheelDelta"in A)?-A.wheelDelta:0},deltaZ:0,deltaMode:0}),xJ=V0($J),kJ=s({},LY,{newState:0,oldState:0}),_J=V0(kJ),PJ=[9,13,27,32],zX=fA&&"CompositionEvent"in window;fA&&"documentMode"in document&&(k6=document.documentMode);gJ=fA&&"TextEvent"in window&&!k6,sZ=fA&&(!zX||k6&&8=k6),B9=String.fromCharCode(32);yJ={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};if(fA){if(fA){if(N6="oninput"in document,!N6)r4=document.createElement("div"),r4.setAttribute("oninput","return;"),N6=typeof r4.oninput==="function";S6=N6}else S6=!1;nZ=S6&&(!document.documentMode||9=document.documentMode;gY={animationend:nA("Animation","AnimationEnd"),animationiteration:nA("Animation","AnimationIteration"),animationstart:nA("Animation","AnimationStart"),transitionrun:nA("Transition","TransitionRun"),transitionstart:nA("Transition","TransitionStart"),transitioncancel:nA("Transition","TransitionCancel"),transitionend:nA("Transition","TransitionEnd")},uH={},YW={};fA&&(YW=document.createElement("div").style,("AnimationEvent"in window)||(delete gY.animationend.animation,delete gY.animationiteration.animation,delete gY.animationstart.animation),("TransitionEvent"in window)||delete gY.transitionend.transition);GW=fY("animationend"),HW=fY("animationiteration"),XW=fY("animationstart"),pJ=fY("transitionrun"),sJ=fY("transitionstart"),rJ=fY("transitioncancel"),wW=fY("transitionend"),ZW=new Map,b8="abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");b8.push("scrollEnd");JG=typeof reportError==="function"?reportError:function(A){if(typeof window==="object"&&typeof window.ErrorEvent==="function"){var Y=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof A==="object"&&A!==null&&typeof A.message==="string"?String(A.message):String(A),error:A});if(!window.dispatchEvent(Y))return}else if(typeof process==="object"&&typeof process.emit==="function"){process.emit("uncaughtException",A);return}console.error(A)},k0=[];EY={};I9=new WeakMap;yY=[],P0=[];S8=Error(F(519));N8=e0(null);aJ=typeof AbortController<"u"?AbortController:function(){var A=[],Y=this.signal={aborted:!1,addEventListener:function(G,H){A.push(H)}};this.abort=function(){Y.aborted=!0,A.forEach(function(G){return G()})}},nJ=KY,tJ=OY,Y0={$$typeof:UA,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};b9=b.S;b.S=function(A,Y){Sq=b0(),typeof Y==="object"&&Y!==null&&typeof Y.then==="function"&&eJ(A,Y),b9!==null&&b9(A,Y)};wY=e0(null);q6=Error(F(460)),bX=Error(F(474)),mG=Error(F(542)),fG={then:function(){}};MY=fW(!0),zW=fW(!1);tY=e0(null),zG=e0(0);$0=e0(null);n=e0(0);e6={readContext:M0,use:cG,useCallback:o,useContext:o,useEffect:o,useImperativeHandle:o,useLayoutEffect:o,useInsertionEffect:o,useMemo:o,useReducer:o,useRef:o,useState:o,useDebugValue:o,useDeferredValue:o,useTransition:o,useSyncExternalStore:o,useId:o,useHostTransitionStatus:o,useFormState:o,useActionState:o,useOptimistic:o,useMemoCache:o,useCacheRefresh:o};e6.useEffectEvent=o;aW={readContext:M0,use:cG,useCallback:function(A,Y){return B0().memoizedState=[A,Y===void 0?null:Y],A},useContext:M0,useEffect:g9,useImperativeHandle:function(A,Y,G){G=G!==null&&G!==void 0?G.concat([A]):null,t4(4194308,4,EW.bind(null,Y,A),G)},useLayoutEffect:function(A,Y){return t4(4194308,4,A,Y)},useInsertionEffect:function(A,Y){t4(4,2,A,Y)},useMemo:function(A,Y){var G=B0();Y=Y===void 0?null:Y;var H=A();if(JY){xA(!0);try{A()}finally{xA(!1)}}return G.memoizedState=[H,Y],H},useReducer:function(A,Y,G){var H=B0();if(G!==void 0){var X=G(Y);if(JY){xA(!0);try{G(Y)}finally{xA(!1)}}}else X=Y;return H.memoizedState=H.baseState=X,A={pending:null,lanes:0,dispatch:null,lastRenderedReducer:A,lastRenderedState:X},H.queue=A,A=A.dispatch=q1.bind(null,S,A),[H.memoizedState,A]},useRef:function(A){var Y=B0();return A={current:A},Y.memoizedState=A},useState:function(A){A=g8(A);var Y=A.queue,G=sW.bind(null,S,Y);return Y.dispatch=G,[A.memoizedState,G]},useDebugValue:hX,useDeferredValue:function(A,Y){var G=B0();return EX(G,A,Y)},useTransition:function(){var A=g8(!1);return A=mW.bind(null,S,A.queue,!0,!1),B0().memoizedState=A,[!1,A]},useSyncExternalStore:function(A,Y,G){var H=S,X=B0();if(_){if(G===void 0)throw Error(F(407));G=G()}else{if(G=Y(),m===null)throw Error(F(349));(k&127)!==0||KW(H,Y,G)}X.memoizedState=G;var w={value:G,getSnapshot:Y};return X.queue=w,g9(bW.bind(null,H,w,A),[A]),H.flags|=2048,eY(9,{destroy:void 0},TW.bind(null,H,w,G,Y),null),G},useId:function(){var A=B0(),Y=m.identifierPrefix;if(_){var G=a0,H=o0;G=(H&~(1<<32-N0(H)-1)).toString(32)+G,Y="_"+Y+"R_"+G,G=FG++,0"u"?null:document;G4={$$typeof:UA,Provider:null,Consumer:null,_currentValue:HY,_currentValue2:HY,_threadCount:0};H4=new Map,X4=new Map,$A=[],wR="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset".split(" ");nG.prototype.render=Yw.prototype.render=function(A){var Y=this._internalRoot;if(Y===null)throw Error(F(409));var G=Y.current,H=j0();HU(G,H,A,Y,null,null)};nG.prototype.unmount=Yw.prototype.unmount=function(){var A=this._internalRoot;if(A!==null){this._internalRoot=null;var Y=A.containerInfo;HU(A.current,2,null,A,null,null),rG(),Y[w6]=null}};nG.prototype.unstable_scheduleHydration=function(A){if(A){var Y=hZ();A={blockedOn:null,target:A,priority:Y};for(var G=0;G<$A.length&&Y!==0&&Y<$A[G].priority;G++);$A.splice(G,0,A),G===0&&ZU(A)}};IZ=gG.version;if(IZ!=="19.2.3")throw Error(F(527,IZ,"19.2.3"));E.findDOMNode=function(A){var Y=A._reactInternals;if(Y===void 0){if(typeof A.render==="function")throw Error(F(188));throw A=Object.keys(A).join(","),Error(F(268,A))}return A=vM(Y),A=A!==null?SZ(A):null,A=A===null?null:A.stateNode,A};qR={bundleType:0,version:"19.2.3",rendererPackageName:"react-dom",currentDispatcherRef:b,reconcilerVersion:"19.2.3"};if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"){if(jY=__REACT_DEVTOOLS_GLOBAL_HOOK__,!jY.isDisabled&&jY.supportsFiber)try{Z4=jY.inject(qR),S0=jY}catch(A){}}});var LU=XH((KR,BU)=>{JU();function RU(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=="function")return;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(RU)}catch(A){console.error(A)}}RU(),BU.exports=Gw});var fw=m0(p0(),1),GH=m0(LU(),1);var D4=m0(p0(),1),Hw={},fU=D4.default.createContext(Hw);function Q4(A){let Y=D4.default.useContext(fU);return D4.default.useMemo(function(){if(typeof A==="function")return A(Y);return{...Y,...A}},[Y,A])}function Xw(A){let Y;if(A.disableParentContext)Y=typeof A.components==="function"?A.components(Hw):A.components||Hw;else Y=Q4(A.components);return D4.default.createElement(fU.Provider,{value:Y},A.children)}var JR=Symbol.for("react.transitional.element"),RR=Symbol.for("react.fragment");function zU(A,Y,G){var H=null;if(G!==void 0&&(H=""+G),Y.key!==void 0&&(H=""+Y.key),"key"in Y){G={};for(var X in Y)X!=="key"&&(G[X]=Y[X])}else G=Y;return Y=G.ref,{$$typeof:JR,type:A,key:H,ref:Y!==void 0?Y:null,props:G}}var F4=RR,M=zU,O=zU;function ww({children:A}){return M("span",{className:"font-bold bg-gradient-to-t from-red-600 via-orange-500 to-yellow-400 bg-clip-text text-transparent animate-pulse",children:A})}var I4=m0(p0(),1);var DU="scratch-demo-todos",V4=null,Zw=new Set;function R6(){if(V4===null)if(typeof window>"u")V4=[];else{let A=localStorage.getItem(DU);V4=A?JSON.parse(A):[]}return V4}function BR(A){if(typeof window>"u")return;localStorage.setItem(DU,JSON.stringify(A))}function LR(){Zw.forEach((A)=>A(R6()))}function fR(){let[A,Y]=I4.useState(()=>R6());I4.useEffect(()=>{Y(R6());let W=(q)=>Y(q);return Zw.add(W),()=>{Zw.delete(W)}},[]);let G=(W)=>{V4=W,BR(W),LR()};return{todos:A,addTodo:(W)=>{if(!W.trim())return;G([...R6(),{id:Date.now(),text:W.trim(),completed:!1}])},toggleTodo:(W)=>{G(R6().map((q)=>q.id===W?{...q,completed:!q.completed}:q))},deleteTodo:(W)=>{G(R6().filter((q)=>q.id!==W))},reset:()=>{G([])}}}function Ww(){let{todos:A,addTodo:Y,toggleTodo:G,deleteTodo:H,reset:X}=fR(),[w,Z]=I4.useState(""),W=()=>{Y(w),Z("")};return O("div",{className:"not-prose border border-gray-200 rounded-lg p-4 my-12 mx-2 sm:mx-8 md:mx-16 bg-gray-50",children:[O("div",{className:"flex gap-2 mb-4",children:[M("input",{type:"text",value:w,onChange:(q)=>Z(q.target.value),onKeyDown:(q)=>q.key==="Enter"&&W(),placeholder:"Add a todo...",className:"flex-1 px-3 py-2 border border-gray-300 rounded-md bg-white text-gray-900 placeholder-gray-400"}),M("button",{onClick:W,className:"px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 transition-colors",children:"Add"})]}),A.length===0?M("p",{className:"text-gray-500 text-center py-4",children:"No todos yet. Add one above!"}):M("ul",{className:"space-y-2",children:A.map((q)=>O("li",{className:"flex items-center gap-3 p-2 rounded-md hover:bg-gray-100",children:[M("input",{type:"checkbox",checked:q.completed,onChange:()=>G(q.id),className:"w-4 h-4 rounded border-gray-300"}),M("span",{className:`flex-1 ${q.completed?"line-through text-gray-400":"text-gray-900"}`,children:q.text}),M("button",{onClick:()=>H(q.id),className:"text-gray-400 hover:text-red-500 transition-colors","aria-label":"Delete todo",children:M("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor",children:M("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})})]},q.id))}),O("div",{className:"mt-4 pt-4 border-t border-gray-200 flex justify-between items-center",children:[O("span",{className:"text-sm text-gray-500",children:[A.filter((q)=>!q.completed).length," remaining"]}),M("button",{onClick:X,className:"text-sm text-gray-500 hover:text-gray-700 transition-colors",children:"Reset"})]})]})}var IA=m0(p0(),1);function qw(){let A=IA.useRef(null),[Y,G]=IA.useState(0),[H,X]=IA.useState({x:20,y:20}),[w,Z]=IA.useState({x:2,y:1.5}),[W,q]=IA.useState("#8b5cf6"),R=80,z=40,Q=["#8b5cf6","#ec4899","#f59e0b","#10b981","#3b82f6","#ef4444"],L=()=>{let f=Q[Math.floor(Math.random()*Q.length)];return f===W?Q[(Q.indexOf(W)+1)%Q.length]:f};return IA.useEffect(()=>{let f=A.current;if(!f)return;let T=setInterval(()=>{X((P)=>{let{clientWidth:J,clientHeight:U}=f,B=P.x+w.x,D=P.y+w.y,K=!1,$=!1;if(B<=0)B=0,K=!0,Z((V)=>({...V,x:Math.abs(V.x)})),q(L());else if(B>=J-80)B=J-80,K=!0,Z((V)=>({...V,x:-Math.abs(V.x)})),q(L());if(D<=0){if(D=0,$=!0,Z((V)=>({...V,y:Math.abs(V.y)})),!K)q(L())}else if(D>=U-40){if(D=U-40,$=!0,Z((V)=>({...V,y:-Math.abs(V.y)})),!K)q(L())}if(K&&$)G((V)=>V+1);return{x:B,y:D}})},16);return()=>clearInterval(T)},[w]),O("div",{className:"not-prose my-6 mx-12",children:[M("div",{className:"bg-gradient-to-b from-gray-700 via-gray-800 to-gray-900 p-3 rounded-2xl shadow-xl",children:M("div",{className:"bg-black p-1 rounded-lg",children:O("div",{ref:A,className:"relative w-full h-48 rounded overflow-hidden",style:{background:"linear-gradient(145deg, #1a1a2e 0%, #0f0f1a 50%, #1a1a2e 100%)"},children:[M("div",{className:"absolute",style:{left:H.x,top:H.y,width:80,height:40},children:M("div",{className:"transition-colors duration-300",style:{width:"100%",height:"100%",backgroundColor:W,maskImage:"url(/DVD_logo.svg)",maskSize:"contain",maskRepeat:"no-repeat",maskPosition:"center",WebkitMaskImage:"url(/DVD_logo.svg)",WebkitMaskSize:"contain",WebkitMaskRepeat:"no-repeat",WebkitMaskPosition:"center"}})}),M("div",{className:"absolute inset-0 pointer-events-none",style:{background:"linear-gradient(135deg, rgba(255,255,255,0.08) 0%, transparent 50%, transparent 100%)"}})]})})}),O("p",{className:"text-center text-sm text-gray-500 mt-3",children:["Corner hits: ",Y]})]})}function zR(A){let Y=A.trim().split(` -`),G=[],H=[];for(let X of Y){if(!X.trim())continue;let w=X.match(/[^\s│|├└─]/);if(!w)continue;let Z=w.index,W=X.slice(0,Z),q=X.slice(Z).trim();if(!q)continue;let R=W.includes("└"),z=W.includes("├")||W.includes("└"),Q=(W.match(/[│|]/g)||[]).length,L;if(!z&&W.length===0)L=0,H.length=0;else{while(H.length>0){if(H.filter((P)=>!P.isLast).length<=Q)break;H.pop()}L=H.length+1}if(R)while(H.length>=L)H.pop();let f=H.map((T)=>T.isLast),I=q.endsWith("/")||!q.includes(".");if(G.push({name:q,depth:L,isLast:R,parentIsLast:f}),I)H.push({depth:L,isLast:R})}return G}function DR({node:A}){let Y=A.name.endsWith("/")||!A.name.includes(".");return O("div",{className:"flex items-stretch h-7 font-mono text-sm",children:[A.parentIsLast.map((G,H)=>M("div",{className:"w-5 flex-shrink-0 relative",children:!G&&M("div",{className:"absolute left-2 top-0 bottom-0 w-px bg-gray-300"})},H)),A.depth>0&&O("div",{className:"w-5 flex-shrink-0 relative",children:[M("div",{className:`absolute left-2 w-px bg-gray-300 ${A.isLast?"top-0 h-1/2":"top-0 bottom-0"}`}),M("div",{className:"absolute left-2 top-1/2 w-3 h-px bg-gray-300"})]}),M("span",{className:`flex items-center ${Y?"text-gray-700 font-bold":"text-gray-600"}`,children:A.name})]})}function tG(A){if(typeof A==="string")return A;if(typeof A==="number")return String(A);if(!A)return"";if(Array.isArray(A))return A.map(tG).join(` -`);if(typeof A==="object"&&"props"in A){let Y=A;if(Y.type==="p"||Y.type==="br")return tG(Y.props.children)+` -`;return tG(Y.props.children)}return""}function eG({children:A}){let Y=tG(A),G=zR(Y);return M("div",{className:"not-prose my-6 py-4 px-4",children:G.map((H,X)=>M(DR,{node:H},X))})}var QR=m0(p0(),1);function Uw({children:A}){return O("div",{className:"min-h-screen bg-white prose max-w-2xl mx-auto px-6 py-8",children:[O("div",{className:"not-prose flex justify-center gap-4 mb-4",children:[M("a",{href:"https://github.com/scratch/scratch",target:"_blank",rel:"noopener noreferrer",className:"opacity-30 hover:opacity-100 transition-opacity",children:M("img",{src:"/github-mark.svg",alt:"GitHub",className:"w-6 h-6"})}),M("a",{href:"https://x.com/koomen",target:"_blank",rel:"noopener noreferrer",className:"opacity-30 hover:opacity-100 transition-opacity",children:M("img",{src:"/x-logo.svg",alt:"X",className:"w-6 h-6"})})]}),A,O("footer",{className:"text-center text-gray-400 text-sm mt-16 pb-8",children:["Released under the MIT License",M("br",{}),"Copyright 2025 Pete Koomen"]})]})}function QU(A){let Y={a:"a",code:"code",div:"div",h2:"h2",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...Q4(),...A.components};return O(Uw,{children:[M("div",{className:"not-prose flex flex-col items-center justify-center mb-8",children:M("img",{src:"/scratch-logo.svg",alt:"Scratch",height:"120"})}),M(Y.p,{children:"Scratch compiles Markdown and React into beautiful static websites that can be hosted anywhere."}),M(Y.h2,{children:"Quick Start"}),M(F4,{children:M(Y.pre,{className:"shiki github-light",style:{backgroundColor:"#fff",color:"#24292e"},tabIndex:"0",children:O(Y.code,{children:[M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Install scratch"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"curl"}),M(Y.span,{style:{color:"#005CC5"},children:" -fsSL"}),M(Y.span,{style:{color:"#032F62"},children:" https://scratch.dev/install.sh"}),M(Y.span,{style:{color:"#D73A49"},children:" |"}),M(Y.span,{style:{color:"#6F42C1"},children:" bash"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Create a new project"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" create"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Start the dev server"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" dev"})]})]})})}),M(Y.h2,{children:"What can you do with Scratch?"}),O(Y.p,{children:["Scratch was designed for collaborative writing with coding agents like ",M(Y.a,{href:"https://www.claude.com/product/claude-code",children:"Claude Code"}),". Use your favorite editor to write in ",M(Y.a,{href:"https://daringfireball.net/projects/markdown/",children:"Markdown"})," and embed React components when it's easier to express yourself with code."]}),M(Y.p,{children:"Scratch supports Github-flavored Markdown features like tables and todolists:"}),O(Y.table,{children:[M(Y.thead,{children:O(Y.tr,{children:[M(Y.th,{children:"Feature"}),M(Y.th,{children:"Supported?"})]})}),O(Y.tbody,{children:[O(Y.tr,{children:[O(Y.td,{children:["Compiles ",M(Y.code,{children:".md"})," ",M(Y.code,{children:".mdx"})," ",M(Y.code,{children:".tsx"})," ",M(Y.code,{children:".ts"})," ",M(Y.code,{children:".jsx"})," ",M(Y.code,{children:".js"})]}),M(Y.td,{children:"✅"})]}),O(Y.tr,{children:[M(Y.td,{children:"Dev server with HMR"}),M(Y.td,{children:"✅"})]}),O(Y.tr,{children:[M(Y.td,{children:"Tailwind CSS styling"}),M(Y.td,{children:"✅"})]}),O(Y.tr,{children:[M(Y.td,{children:"Code syntax highlighting"}),M(Y.td,{children:"✅"})]})]})]}),O(Y.p,{children:["Code blocks use syntax highlighting by ",M(Y.a,{href:"https://shiki.style/",children:"Shiki"}),":"]}),M(F4,{children:M(Y.pre,{className:"shiki github-light",style:{backgroundColor:"#fff",color:"#24292e"},tabIndex:"0",children:O(Y.code,{children:[O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#D73A49"},children:"def"}),M(Y.span,{style:{color:"#6F42C1"},children:" greet"}),M(Y.span,{style:{color:"#24292E"},children:"(name: "}),M(Y.span,{style:{color:"#005CC5"},children:"str"}),M(Y.span,{style:{color:"#24292E"},children:") -> "}),M(Y.span,{style:{color:"#005CC5"},children:"str"}),M(Y.span,{style:{color:"#24292E"},children:":"})]}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#D73A49"},children:" return"}),M(Y.span,{style:{color:"#D73A49"},children:" f"}),M(Y.span,{style:{color:"#032F62"},children:'"Hello, '}),M(Y.span,{style:{color:"#005CC5"},children:"{"}),M(Y.span,{style:{color:"#24292E"},children:"name"}),M(Y.span,{style:{color:"#005CC5"},children:"}"}),M(Y.span,{style:{color:"#032F62"},children:'!"'})]}),` -`,M(Y.span,{className:"line"}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#005CC5"},children:"print"}),M(Y.span,{style:{color:"#24292E"},children:"(greet("}),M(Y.span,{style:{color:"#032F62"},children:'"World"'}),M(Y.span,{style:{color:"#24292E"},children:"))"})]})]})})}),O(Y.p,{children:["You can use React components to ",M(ww,{children:"style text"})," or embed fully working demos in your product specs:"]}),M(Y.div,{className:"not-prose",children:M(Ww,{})}),M(Y.p,{children:"In fact, coding agents have gotten so good that if you can describe it, you can add it to a document:"}),M(Y.div,{className:"not-prose",children:M(qw,{})}),M(Y.h2,{children:"No Boilerplate"}),O(Y.p,{children:["Scratch uses an opinionated project structure and requires ",M(Y.strong,{children:"no boilerplate or configuration"}),": just create a project, run the dev server with ",M(Y.code,{children:"scratch dev"}),", and start writing."]}),O(Y.p,{children:["A simple Scratch project (created with ",M(Y.code,{children:"scratch create"}),") looks like this:"]}),M(eG,{children:M(Y.p,{children:`mysite/ -├── pages/ -│ ├── index.mdx -│ ├── Counter.tsx -│ └── examples/ -│ ├── index.md -│ ├── markdown.md -│ ├── todolist-spec.mdx -│ └── TodoList.tsx -└── public/ -├── logo.png -└── favicon.svg`})}),O(Y.p,{children:["Use ",M(Y.code,{children:"scratch build"})," to compile this project into a ",M(Y.a,{href:"https://scratch.dev/template",children:"static website"}),"."]}),O(Y.p,{children:["Borrowing heavily from ",M(Y.a,{href:"https://github.com/tailwindlabs/tailwindcss-typography",children:"Tailwind Typography"}),", Scratch uses default styles and Markdown components to render your prose with a clean aesthetic. Code blocks use syntax highlighting by ",M(Y.a,{href:"https://shiki.style/",children:"Shiki"}),"."]}),O(Y.p,{children:["You can change styles and customize the page wrapper component by including the ",M(Y.code,{children:"src/"})," directory when you run ",M(Y.code,{children:"scratch create"}),":"]}),M(eG,{children:M(Y.p,{children:`mysite/ -├── pages/ -├── public/ -└── src/ -├── markdown/ -├── PageWrapper.tsx -└── tailwind.css`})}),O(Y.p,{children:["Component files and js/ts libraries can live anywhere in ",M(Y.code,{children:"pages/"})," and ",M(Y.code,{children:"src/"}),". They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name."]}),O(Y.p,{children:["Scratch installs build dependencies automatically. You can add additional third-party dependencies by including a ",M(Y.code,{children:"package.json"})," file in your project root."]}),M(Y.h2,{children:"Built on Bun"}),O(Y.p,{children:["Scratch is built on ",M(Y.a,{href:"https://bun.com/",children:"Bun"})," for lightning-fast builds, development with HMR, and native typescript support. It uses the ",M(Y.a,{href:"https://tailwindcss.com/",children:"Tailwind CSS"})," framework to make component styling easy."]}),M(Y.p,{children:"Scratch compiles Javascript (.js), Typescript (.ts), JSX (.jsx), TSX (.tsx), Markdown (.md), and MDX (.mdx)."}),M(Y.h2,{children:"Commands"}),M(F4,{children:M(Y.pre,{className:"shiki github-light",style:{backgroundColor:"#fff",color:"#24292e"},tabIndex:"0",children:O(Y.code,{children:[M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Create a new project"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" create"}),M(Y.span,{style:{color:"#032F62"},children:" my-site"}),M(Y.span,{style:{color:"#6A737D"},children:" # interactive"})]}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" create"}),M(Y.span,{style:{color:"#005CC5"},children:" --minimal"}),M(Y.span,{style:{color:"#6A737D"},children:" # omit src/ and page examples"})]}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" create"}),M(Y.span,{style:{color:"#005CC5"},children:" --full"}),M(Y.span,{style:{color:"#6A737D"},children:" # include src/, examples, and package.json"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Start dev server with hot module reloading"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" dev"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Build for production"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" build"})]}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" build"}),M(Y.span,{style:{color:"#005CC5"},children:" --ssg"}),M(Y.span,{style:{color:"#005CC5"},children:" false"}),M(Y.span,{style:{color:"#6A737D"},children:" # disable static site generation"})]}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" build"}),M(Y.span,{style:{color:"#005CC5"},children:" --development"}),M(Y.span,{style:{color:"#6A737D"},children:" # unminified, with source maps"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Preview production build locally"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" preview"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Remove build artifacts"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" clean"})]}),` -`,M(Y.span,{className:"line"}),` -`,M(Y.span,{className:"line",children:M(Y.span,{style:{color:"#6A737D"},children:"# Update scratch to latest version"})}),` -`,O(Y.span,{className:"line",children:[M(Y.span,{style:{color:"#6F42C1"},children:"scratch"}),M(Y.span,{style:{color:"#032F62"},children:" update"})]})]})})})]})}function Mw(A={}){let{wrapper:Y}={...Q4(),...A.components};return Y?M(Y,{...A,children:M(QU,{...A})}):QU(A)}var AH=m0(p0(),1);function Jw({children:A,className:Y,style:G,...H}){let[X,w]=AH.useState(!1),Z=AH.useRef(null);return O("div",{className:"relative group",children:[M("button",{onClick:async()=>{if(!Z.current)return;let q=Z.current.textContent||"";await navigator.clipboard.writeText(q),w(!0),setTimeout(()=>w(!1),2000)},className:"copy-button","aria-label":"Copy code",children:X?"Copied!":"Copy"}),M("pre",{ref:Z,className:Y,style:G,...H,children:A})]})}var FU=m0(p0(),1);function FR(A){return A.toString().toLowerCase().trim().replace(/\s+/g,"-").replace(/[^\w\-]+/g,"").replace(/\-\-+/g,"-")}function Rw(A){if(typeof A==="string")return A;if(Array.isArray(A))return A.map(Rw).join("");if(FU.default.isValidElement(A)&&A.props?.children)return Rw(A.props.children);return""}function VU({children:A,level:Y}){let G=Rw(A),H=FR(G),X=`h${Y}`;return O(X,{id:H,className:"group relative",children:[M("a",{href:`#${H}`,className:"heading-anchor","aria-label":`Link to ${G}`,children:"#"}),A]})}function Bw(A){return M(VU,{...A,level:2})}function Lw(A){return M(VU,{...A,level:3})}function YH({href:A,children:Y,...G}){let H=A?.startsWith("http://")||A?.startsWith("https://");return M("a",{href:A,...H&&{target:"_blank",rel:"noopener noreferrer"},...G,children:Y})}var IU={pre:Jw,h2:Bw,h3:Lw,a:YH};var OU=fw.default.createElement(Xw,{components:IU},fw.default.createElement(Mw)),KU=document.getElementById("mdx");if(window.__scratch_ssg)console.log("Hydrating mdx component"),GH.hydrateRoot(KU,OU);else console.log("Rendering mdx component"),GH.createRoot(KU).render(OU); diff --git a/dist/index.html b/dist/index.html deleted file mode 100644 index 3487831..0000000 --- a/dist/index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - Scratch - - - - - - - - - - - -
GitHubX
Scratch

Scratch compiles Markdown and React into beautiful static websites that can be hosted anywhere.

#Quick Start

# Install scratch
-curl -fsSL https://scratch.dev/install.sh | bash
-
-# Create a new project
-scratch create
-
-# Start the dev server
-scratch dev

#What can you do with Scratch?

Scratch was designed for collaborative writing with coding agents like Claude Code. Use your favorite editor to write in Markdown and embed React components when it's easier to express yourself with code.

Scratch supports Github-flavored Markdown features like tables and todolists:

FeatureSupported?
Compiles .md .mdx .tsx .ts .jsx .js
Dev server with HMR
Tailwind CSS styling
Code syntax highlighting

Code blocks use syntax highlighting by Shiki:

def greet(name: str) -> str:
-    return f"Hello, {name}!"
-
-print(greet("World"))

You can use React components to style text or embed fully working demos in your product specs:

No todos yet. Add one above!

0 remaining

In fact, coding agents have gotten so good that if you can describe it, you can add it to a document:

Corner hits: 0

#No Boilerplate

Scratch uses an opinionated project structure and requires no boilerplate or configuration: just create a project, run the dev server with scratch dev, and start writing.

A simple Scratch project (created with scratch create) looks like this:

mysite/
pages/
index.mdx
Counter.tsx
examples/
index.md
markdown.md
todolist-spec.mdx
TodoList.tsx
public/
logo.png
favicon.svg

Use scratch build to compile this project into a static website.

Borrowing heavily from Tailwind Typography, Scratch uses default styles and Markdown components to render your prose with a clean aesthetic. Code blocks use syntax highlighting by Shiki.

You can change styles and customize the page wrapper component by including the src/ directory when you run scratch create:

mysite/
pages/
public/
src/
markdown/
PageWrapper.tsx
tailwind.css

Component files and js/ts libraries can live anywhere in pages/ and src/. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name.

Scratch installs build dependencies automatically. You can add additional third-party dependencies by including a package.json file in your project root.

#Built on Bun

Scratch is built on Bun for lightning-fast builds, development with HMR, and native typescript support. It uses the Tailwind CSS framework to make component styling easy.

Scratch compiles Javascript (.js), Typescript (.ts), JSX (.jsx), TSX (.tsx), Markdown (.md), and MDX (.mdx).

#Commands

# Create a new project
-scratch create my-site    # interactive
-scratch create --minimal  # omit src/ and page examples
-scratch create --full     # include src/, examples, and package.json
-
-# Start dev server with hot module reloading
-scratch dev
-
-# Build for production
-scratch build
-scratch build --ssg false    # disable static site generation
-scratch build --development  # unminified, with source maps
-
-# Preview production build locally
-scratch preview
-
-# Remove build artifacts
-scratch clean
-
-# Update scratch to latest version
-scratch update
Released under the MIT License
Copyright 2025 Pete Koomen
- - - \ No newline at end of file diff --git a/dist/scratch-logo.svg b/dist/scratch-logo.svg deleted file mode 100644 index c44fcb1..0000000 --- a/dist/scratch-logo.svg +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Scratch - diff --git a/dist/tailwind-510c5f85.css b/dist/tailwind-510c5f85.css deleted file mode 100644 index df53666..0000000 --- a/dist/tailwind-510c5f85.css +++ /dev/null @@ -1,2 +0,0 @@ -/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-leading:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-orange-500:oklch(70.5% .213 47.604);--color-yellow-400:oklch(85.2% .199 91.936);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.bottom-0{bottom:calc(var(--spacing)*0)}.left-2{left:calc(var(--spacing)*2)}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-12{margin-inline:calc(var(--spacing)*12)}.mx-auto{margin-inline:auto}.my-6{margin-block:calc(var(--spacing)*6)}.my-12{margin-block:calc(var(--spacing)*12)}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);margin-top:1.2em;margin-bottom:1.2em;font-size:1.25em;line-height:1.6}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:decimal}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:disc}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.25em;font-weight:600}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em;font-style:italic;font-weight:500}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:0;margin-bottom:.888889em;font-size:2.25em;font-weight:800;line-height:1.11111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:2em;margin-bottom:1em;font-size:1.5em;font-weight:700;line-height:1.33333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.6em;margin-bottom:.6em;font-size:1.25em;font-weight:600;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em;display:block}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;border-radius:.3125rem;padding-inline-start:.375em;font-family:inherit;font-size:.875em;font-weight:500}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);padding-top:.857143em;padding-inline-end:1.14286em;padding-bottom:.857143em;border-radius:.375rem;margin-top:1.71429em;margin-bottom:1.71429em;padding-inline-start:1.14286em;font-size:.875em;font-weight:400;line-height:1.71429;overflow-x:auto}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit;background-color:#0000;border-width:0;border-radius:0;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){table-layout:auto;width:100%;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.71429}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);vertical-align:bottom;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em;font-weight:600}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);margin-top:.857143em;font-size:.875em;line-height:1.42857}.prose{--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:oklab(21% -.00316127 -.0338527/.1);--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:#ffffff1a;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.571429em;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-16{margin-top:calc(var(--spacing)*16)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.block{display:block}.flex{display:flex}.inline{display:inline}.h-1\/2{height:50%}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-48{height:calc(var(--spacing)*48)}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.flex-col{flex-direction:column}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.bg-black{background-color:var(--color-black)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-300{background-color:var(--color-gray-300)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-white{background-color:var(--color-white)}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-gray-700{--tw-gradient-from:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-red-600{--tw-gradient-from:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.via-gray-800{--tw-gradient-via:var(--color-gray-800);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-orange-500{--tw-gradient-via:var(--color-orange-500);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-gray-900{--tw-gradient-to:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-yellow-400{--tw-gradient-to:var(--color-yellow-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-8{padding:calc(var(--spacing)*8)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-2{padding-block:calc(var(--spacing)*2)}.py-4{padding-block:calc(var(--spacing)*4)}.py-8{padding-block:calc(var(--spacing)*8)}.pt-4{padding-top:calc(var(--spacing)*4)}.pb-8{padding-bottom:calc(var(--spacing)*8)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-900{color:var(--color-gray-900)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.placeholder-gray-400::placeholder{color:var(--color-gray-400)}.opacity-30{opacity:.3}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}@media (hover:hover){.hover\:border-gray-400:hover{border-color:var(--color-gray-400)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}@media (min-width:40rem){.sm\:mx-8{margin-inline:calc(var(--spacing)*8)}}@media (min-width:48rem){.md\:mx-16{margin-inline:calc(var(--spacing)*16)}}}.prose :where(code):not(:where([class~=not-prose],pre *)){background-color:var(--color-gray-100);padding-inline:calc(var(--spacing)*1.5);padding-block:calc(var(--spacing)*.5);--tw-font-weight:var(--font-weight-medium);font-size:.9em;font-weight:var(--font-weight-medium);color:var(--color-gray-900);border-radius:.25rem}.prose :where(code):not(:where([class~=not-prose],pre *)):before,.prose :where(code):not(:where([class~=not-prose],pre *)):after{content:none}.prose :where(pre code):not(:where([class~=not-prose] *)){border-style:var(--tw-border-style);padding:calc(var(--spacing)*0);--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal);font-size:inherit;background-color:#0000;border-width:0;border-radius:0}.prose :where(kbd):not(:where([class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing)*1.5);padding-block:calc(var(--spacing)*.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-radius:.25rem}.prose :where(ul.contains-task-list):not(:where([class~=not-prose] *)){padding-left:calc(var(--spacing)*0);list-style-type:none}.prose :where(li.task-list-item>input[type=checkbox]):not(:where([class~=not-prose] *)){margin-right:calc(var(--spacing)*2);margin-left:calc(var(--spacing)*1);vertical-align:middle}.prose :where(table):not(:where([class~=not-prose] *)){margin-inline:calc(var(--spacing)*4);width:auto;max-width:100%}pre:has(>code){border-radius:var(--radius-lg);background-color:var(--color-white);overflow-x:auto}pre>code{padding:calc(var(--spacing)*4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed);color:#24292e;display:block}.copy-button{top:calc(var(--spacing)*2);right:calc(var(--spacing)*2);padding-inline:calc(var(--spacing)*2);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));opacity:0;transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:.25rem;position:absolute}@media (hover:hover){.copy-button:is(:where(.group):hover *){opacity:1}}.copy-button{background-color:var(--color-gray-200);color:var(--color-gray-500)}@media (hover:hover){.copy-button:hover{color:var(--color-gray-700)}}.heading-anchor{top:calc(var(--spacing)*0);left:calc(var(--spacing)*-6);opacity:0;transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));-webkit-user-select:none;user-select:none;text-decoration-line:none;position:absolute}@media (hover:hover){.heading-anchor:is(:where(.group):hover *){opacity:1}}.heading-anchor{color:var(--color-gray-400)}@media (hover:hover){.heading-anchor:hover{color:var(--color-gray-600)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@property --tw-leading{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} \ No newline at end of file diff --git a/dist/x-logo.svg b/dist/x-logo.svg deleted file mode 100644 index 56368dd..0000000 --- a/dist/x-logo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - From 60db026844939f9af015324b73908be59ae1cd05 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Tue, 23 Dec 2025 01:35:44 -0800 Subject: [PATCH 02/40] add dist to gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6127d6e..ed7273e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,7 @@ node_modules # output out -# check in compiled output for now; cloudflare pages can't install scratch to build this -# dist +dist *.tgz # code coverage From adddddd63a713195c21f76c4861b9d010aaf15ac Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Tue, 23 Dec 2025 01:39:41 -0800 Subject: [PATCH 03/40] add revert documentation --- pages/index.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pages/index.mdx b/pages/index.mdx index 67e6c5f..49f6f6e 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -125,6 +125,11 @@ scratch preview # Remove build artifacts scratch clean +# Revert a file to its template version +scratch revert [file] # Revert a file to its template version +scratch revert [dir] # Revert files in to their template version +scratch revert --list # list available template files + # Update scratch to latest version scratch update ``` From d45f56073bd91785827dbc2b02c74e52c73538cb Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 02:25:54 -0800 Subject: [PATCH 04/40] update command docs --- pages/index.mdx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pages/index.mdx b/pages/index.mdx index 49f6f6e..1ebd9f5 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -107,17 +107,14 @@ Scratch compiles Javascript (.js), Typescript (.ts), JSX (.jsx), TSX (.tsx), Mar ```bash # Create a new project -scratch create my-site # interactive -scratch create --minimal # omit src/ and page examples -scratch create --full # include src/, examples, and package.json +scratch create [path] # create project at path (default: current directory) # Start dev server with hot module reloading scratch dev # Build for production scratch build -scratch build --ssg false # disable static site generation -scratch build --development # unminified, with source maps +scratch build --no-ssg # disable static site generation # Preview production build locally scratch preview @@ -126,9 +123,9 @@ scratch preview scratch clean # Revert a file to its template version -scratch revert [file] # Revert a file to its template version -scratch revert [dir] # Revert files in to their template version -scratch revert --list # list available template files +scratch revert [file] # revert a file to its template version +scratch revert [file]--force # overwrite without confirmation +scratch revert --list # list available template files # Update scratch to latest version scratch update From f88d292309f09fe4866618221540566b72ea04e2 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 02:32:26 -0800 Subject: [PATCH 05/40] update scratch project file section --- pages/index.mdx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pages/index.mdx b/pages/index.mdx index 1ebd9f5..ba14e96 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -79,18 +79,19 @@ A simple Scratch project (created with `scratch create`) looks like this: Use `scratch build` to compile this project into a [static website](https://scratch.dev/template). -Borrowing heavily from [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography), Scratch uses default styles and Markdown components to render your prose with a clean aesthetic. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). +Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography), to render your prose with a clean aesthetic. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). -You can change styles and customize the page wrapper component by including the `src/` directory when you run `scratch create`: +You can change styles and customize the page wrapper component by modifying the files in the `src/` directory: mysite/ ├── pages/ ├── public/ - └── src/ - ├── markdown/ - ├── PageWrapper.tsx - └── tailwind.css + ├── src/ + │ ├── markdown/ + │ ├── PageWrapper.tsx + │ └── tailwind.css + └── package.json Component files and js/ts libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. @@ -115,6 +116,7 @@ scratch dev # Build for production scratch build scratch build --no-ssg # disable static site generation +scratch build --development # unminified, with source maps # Preview production build locally scratch preview From d8174a491886705541eb4e4780e6692902d25d73 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 02:32:56 -0800 Subject: [PATCH 06/40] fix commands typo --- pages/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/index.mdx b/pages/index.mdx index ba14e96..0428709 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -126,7 +126,7 @@ scratch clean # Revert a file to its template version scratch revert [file] # revert a file to its template version -scratch revert [file]--force # overwrite without confirmation +scratch revert [file] --force # overwrite without confirmation scratch revert --list # list available template files # Update scratch to latest version From f8e0ab02ddda174bf3551ff8509626533610b289 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 02:34:40 -0800 Subject: [PATCH 07/40] swap flag and [file] in commands section --- pages/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/index.mdx b/pages/index.mdx index 0428709..88422df 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -126,7 +126,7 @@ scratch clean # Revert a file to its template version scratch revert [file] # revert a file to its template version -scratch revert [file] --force # overwrite without confirmation +scratch revert --force [file] # overwrite without confirmation scratch revert --list # list available template files # Update scratch to latest version From c61a74905d98d8da12fb20dcf1462e44fec1c7d1 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 11:11:11 -0800 Subject: [PATCH 08/40] update files component --- pages/Files.tsx | 265 ++++++++++++++++++++++++++++++++---------------- pages/index.mdx | 87 ++++++++-------- 2 files changed, 223 insertions(+), 129 deletions(-) diff --git a/pages/Files.tsx b/pages/Files.tsx index 892dbf4..659e320 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -1,115 +1,182 @@ -import React from "react"; +import React, { useState } from "react"; -interface FileNode { +// Tree node structure from parsing +interface TreeNode { + id: string; + name: string; + children: TreeNode[]; + startCollapsed: boolean; +} + +// Flattened node for rendering +interface RenderNode { + id: string; name: string; depth: number; isLast: boolean; parentIsLast: boolean[]; + isFolder: boolean; + hasChildren: boolean; } -function parseTree(text: string): FileNode[] { - const lines = text.trim().split("\n"); - const nodes: FileNode[] = []; - - // Stack of { depth, isLast } for each open folder level - const stack: { depth: number; isLast: boolean }[] = []; - - for (const line of lines) { - if (!line.trim()) continue; +function parseTree(text: string): TreeNode[] { + const lines = text.split("\n").filter((line) => line.trim().length > 0); + if (lines.length === 0) return []; - // Find where the actual filename starts - const nameMatch = line.match(/[^\s│|├└─]/); - if (!nameMatch) continue; + // Parse lines - support both whitespace and dot-prefix for indentation + const items = lines.map((line, index) => { + const dotMatch = line.match(/^(\.+)/); + let name: string; + let indent: number; - const nameStart = nameMatch.index!; - const prefix = line.slice(0, nameStart); - const name = line.slice(nameStart).trim(); + if (dotMatch) { + name = line.slice(dotMatch[1].length); + indent = dotMatch[1].length; + } else { + name = line.trim(); + indent = line.length - line.trimStart().length; + } - if (!name) continue; + // Check for (collapsed) suffix + const collapsedMatch = name.match(/\s*\(collapsed\)\s*$/i); + const startCollapsed = !!collapsedMatch; + if (collapsedMatch) { + name = name.slice(0, collapsedMatch.index).trim(); + } - const isLast = prefix.includes("└"); - const hasBranch = prefix.includes("├") || prefix.includes("└"); + return { name, indent, startCollapsed, lineIndex: index }; + }); - // Count │ to know how many non-last ancestors - const pipeCount = (prefix.match(/[│|]/g) || []).length; + // Normalize indents by subtracting the minimum + const baseIndent = Math.min(...items.map((item) => item.indent)); + items.forEach((item) => (item.indent -= baseIndent)); - let depth: number; + // Build tree using a stack + const roots: TreeNode[] = []; + const stack: { node: TreeNode; indent: number }[] = []; - if (!hasBranch && prefix.length === 0) { - // Root item - depth = 0; - stack.length = 0; - } else { - // Pop stack until we find the right parent level - // pipeCount tells us how many ancestors are still "open" (not last) - while (stack.length > 0) { - const openCount = stack.filter((s) => !s.isLast).length; - if (openCount <= pipeCount) break; - stack.pop(); - } + for (const { name, indent, startCollapsed, lineIndex } of items) { + const node: TreeNode = { + id: `node-${lineIndex}`, + name, + children: [], + startCollapsed, + }; - depth = stack.length + 1; + // Pop stack until we find the parent (item with smaller indent) + while (stack.length > 0 && stack[stack.length - 1].indent >= indent) { + stack.pop(); } - // Update stack - if (isLast) { - // Pop items at this depth or deeper - while (stack.length >= depth) { - stack.pop(); - } + if (stack.length === 0) { + roots.push(node); + } else { + stack[stack.length - 1].node.children.push(node); } - // Build parentIsLast array from stack - const parentIsLast = stack.map((s) => s.isLast); + stack.push({ node, indent }); + } - const isFolder = name.endsWith("/") || !name.includes("."); + return roots; +} - nodes.push({ - name, - depth, - isLast, - parentIsLast, +function flattenTree( + roots: TreeNode[], + collapsedIds: Set +): RenderNode[] { + const result: RenderNode[] = []; + + function traverse( + nodes: TreeNode[], + depth: number, + parentIsLast: boolean[] + ): void { + nodes.forEach((node, index) => { + const isLast = index === nodes.length - 1; + const isFolder = node.name.endsWith("/") || !node.name.includes("."); + const hasChildren = node.children.length > 0; + + result.push({ + id: node.id, + name: node.name, + depth, + isLast, + parentIsLast: [...parentIsLast], + isFolder, + hasChildren, + }); + + // Only traverse children if not collapsed + if (hasChildren && !collapsedIds.has(node.id)) { + traverse(node.children, depth + 1, [...parentIsLast, isLast]); + } }); + } + + traverse(roots, 0, []); + return result; +} + +function getInitialCollapsed(roots: TreeNode[]): Set { + const collapsed = new Set(); - // If this is a folder, push to stack for potential children - if (isFolder) { - stack.push({ depth, isLast }); + function traverse(nodes: TreeNode[]): void { + for (const node of nodes) { + if (node.startCollapsed) { + collapsed.add(node.id); + } + traverse(node.children); } } - return nodes; + traverse(roots); + return collapsed; } -function FileRow({ node }: { node: FileNode }) { - const isFolder = node.name.endsWith("/") || !node.name.includes("."); +interface FileRowProps { + node: RenderNode; + isCollapsed: boolean; + onToggle: () => void; +} - return ( -
- {/* Render vertical lines for each parent level */} - {node.parentIsLast.map((parentLast, i) => ( -
- {!parentLast && ( -
- )} -
- ))} +function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { + const isClickable = node.isFolder && node.hasChildren; - {/* Render the branch for this node */} - {node.depth > 0 && ( -
- {/* Vertical line (full height if not last, half if last) */} -
- {/* Horizontal line */} -
-
+ return ( +
+ {/* Caret for folders with children */} + {isClickable ? ( + + ) : ( +
)} - {/* File/folder name */} - + {node.name}
@@ -127,7 +194,6 @@ function extractText(children: React.ReactNode): string { if (typeof children === "object" && "props" in children) { const el = children as React.ReactElement; - // If it's a

tag or similar, extract and add newline if (el.type === "p" || el.type === "br") { return extractText(el.props.children) + "\n"; } @@ -137,14 +203,41 @@ function extractText(children: React.ReactNode): string { return ""; } -export default function Files({ children }: { children: React.ReactNode }) { - const text = extractText(children); - const nodes = parseTree(text); +interface FilesProps { + content?: string; + children?: React.ReactNode; +} + +export default function Files({ content, children }: FilesProps) { + const text = content ?? extractText(children); + const [tree] = useState(() => parseTree(text)); + const [collapsedIds, setCollapsedIds] = useState(() => + getInitialCollapsed(tree) + ); + + const nodes = flattenTree(tree, collapsedIds); + + const toggleCollapse = (id: string) => { + setCollapsedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) { + next.delete(id); + } else { + next.add(id); + } + return next; + }); + }; return (

- {nodes.map((node, i) => ( - + {nodes.map((node) => ( + toggleCollapse(node.id)} + /> ))}
); diff --git a/pages/index.mdx b/pages/index.mdx index 88422df..4265950 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -28,18 +28,17 @@ scratch dev ## What can you do with Scratch? -Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in [Markdown](https://daringfireball.net/projects/markdown/) and embed React components when it's easier to express yourself with code. +Scratch lets you write in Markdown and embed interactive React components, like this counter: -Scratch supports Github-flavored Markdown features like tables and todolists: + -| Feature | Supported? | -|---------|-----------| -| Compiles `.md` `.mdx` `.tsx` `.ts` `.jsx` `.js` | ✅ | -| Dev server with HMR | ✅ | -| Tailwind CSS styling | ✅ | -| Code syntax highlighting | ✅ | +Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask a coding agent for help when it's easier to express yourself with code. + +You can use React components to style text or embed fully working demos in your product specs: + + -Code blocks use syntax highlighting by [Shiki](https://shiki.style/): +Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography), to render your prose with a clean aesthetic. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). ```python def greet(name: str) -> str: @@ -48,51 +47,53 @@ def greet(name: str) -> str: print(greet("World")) ``` -You can use React components to style text or embed fully working demos in your product specs: +Scratch also supports Github-flavored Markdown features like checklists and tables: - +| Feature | Supported? | +|---------|-----------| +| Compiles Markdown, TS, JS & CSS | ✅ | +| Dev server with HMR | ✅ | +| Tailwind CSS styling | ✅ | +| Code syntax highlighting | ✅ | -In fact, coding agents have gotten so good that if you can describe it, you can add it to a document: +Unlike traditional word processors, Scratch makes it easy to express any idea. If you can describe it to a coding agent, you can add it to your document: + +```text +Make me a component that looks like a TV screen with a bouncing DVD logo. Count the number of times the logo hits a corner and display that below the TV. +``` +Collaborating with AI makes writing more fun. Scratch makes that easy. + ## No Boilerplate Scratch uses an opinionated project structure and requires **no boilerplate or configuration**: just create a project, run the dev server with `scratch dev`, and start writing. A simple Scratch project (created with `scratch create`) looks like this: - - mysite/ - ├── pages/ - │ ├── index.mdx - │ ├── Counter.tsx - │ └── examples/ - │ ├── index.md - │ ├── markdown.md - │ ├── todolist-spec.mdx - │ └── TodoList.tsx - └── public/ - ├── logo.png - └── favicon.svg - - -Use `scratch build` to compile this project into a [static website](https://scratch.dev/template). - -Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography), to render your prose with a clean aesthetic. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). - -You can change styles and customize the page wrapper component by modifying the files in the `src/` directory: - - - mysite/ - ├── pages/ - ├── public/ - ├── src/ - │ ├── markdown/ - │ ├── PageWrapper.tsx - │ └── tailwind.css - └── package.json - + + +Use `scratch build` to compile this project into a static website, like this one. You can change styles and customize the page wrapper component by modifying the files in the `src/` directory: + + Component files and js/ts libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. From ee156860151a35cf4e2476e0ef8498f66e33d54b Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 11:19:01 -0800 Subject: [PATCH 09/40] more files tweaks --- pages/Files.tsx | 31 +++++++++++++++++++++++-------- pages/index.mdx | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pages/Files.tsx b/pages/Files.tsx index 659e320..4c158b1 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -4,6 +4,7 @@ import React, { useState } from "react"; interface TreeNode { id: string; name: string; + comment?: string; children: TreeNode[]; startCollapsed: boolean; } @@ -12,6 +13,7 @@ interface TreeNode { interface RenderNode { id: string; name: string; + comment?: string; depth: number; isLast: boolean; parentIsLast: boolean[]; @@ -28,6 +30,7 @@ function parseTree(text: string): TreeNode[] { const dotMatch = line.match(/^(\.+)/); let name: string; let indent: number; + let comment: string | undefined; if (dotMatch) { name = line.slice(dotMatch[1].length); @@ -37,6 +40,13 @@ function parseTree(text: string): TreeNode[] { indent = line.length - line.trimStart().length; } + // Check for # comment + const commentMatch = name.match(/\s*#\s*(.*)$/); + if (commentMatch) { + comment = commentMatch[1].trim(); + name = name.slice(0, commentMatch.index).trim(); + } + // Check for (collapsed) suffix const collapsedMatch = name.match(/\s*\(collapsed\)\s*$/i); const startCollapsed = !!collapsedMatch; @@ -44,7 +54,7 @@ function parseTree(text: string): TreeNode[] { name = name.slice(0, collapsedMatch.index).trim(); } - return { name, indent, startCollapsed, lineIndex: index }; + return { name, indent, comment, startCollapsed, lineIndex: index }; }); // Normalize indents by subtracting the minimum @@ -55,10 +65,11 @@ function parseTree(text: string): TreeNode[] { const roots: TreeNode[] = []; const stack: { node: TreeNode; indent: number }[] = []; - for (const { name, indent, startCollapsed, lineIndex } of items) { + for (const { name, indent, comment, startCollapsed, lineIndex } of items) { const node: TreeNode = { id: `node-${lineIndex}`, name, + comment, children: [], startCollapsed, }; @@ -93,12 +104,13 @@ function flattenTree( ): void { nodes.forEach((node, index) => { const isLast = index === nodes.length - 1; - const isFolder = node.name.endsWith("/") || !node.name.includes("."); + const isFolder = node.name.endsWith("/"); const hasChildren = node.children.length > 0; result.push({ id: node.id, name: node.name, + comment: node.comment, depth, isLast, parentIsLast: [...parentIsLast], @@ -140,14 +152,14 @@ interface FileRowProps { } function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { - const isClickable = node.isFolder && node.hasChildren; + const isClickable = node.isFolder; return (
- {/* Caret for folders with children */} + {/* Caret for folders */} {isClickable ? (
); } diff --git a/pages/index.mdx b/pages/index.mdx index 4265950..e5bb060 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -74,7 +74,7 @@ A simple Scratch project (created with `scratch create`) looks like this: Date: Wed, 24 Dec 2025 11:46:51 -0800 Subject: [PATCH 10/40] more index.mdx and Files cleanup --- bun.lock | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 16 +++++ pages/Files.tsx | 90 ++++++++++++----------- pages/index.mdx | 62 +++++++++------- 4 files changed, 286 insertions(+), 66 deletions(-) create mode 100644 bun.lock create mode 100644 package.json diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..18a0122 --- /dev/null +++ b/bun.lock @@ -0,0 +1,184 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "scratch.dev", + "dependencies": { + "@mdx-js/react": "latest", + "@tailwindcss/cli": "latest", + "@tailwindcss/typography": "latest", + "react": "latest", + "react-dom": "latest", + "tailwindcss": "latest", + }, + }, + }, + "packages": { + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], + + "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="], + + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], + + "@tailwindcss/cli": ["@tailwindcss/cli@4.1.18", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "enhanced-resolve": "^5.18.3", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.18" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-sMZ+lZbDyxwjD2E0L7oRUjJ01Ffjtme5OtjvvnC+cV4CEDcbqzbp25TCpxHj6kWLU9+DlqJOiNgSOgctC2aZmg=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], + + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + + "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1194b55 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "scratch.dev", + "private": true, + "scripts": { + "dev": "scratch dev", + "build": "scratch build" + }, + "dependencies": { + "react": "latest", + "react-dom": "latest", + "@mdx-js/react": "latest", + "tailwindcss": "latest", + "@tailwindcss/cli": "latest", + "@tailwindcss/typography": "latest" + } +} diff --git a/pages/Files.tsx b/pages/Files.tsx index 4c158b1..630cf98 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -25,16 +25,16 @@ function parseTree(text: string): TreeNode[] { const lines = text.split("\n").filter((line) => line.trim().length > 0); if (lines.length === 0) return []; - // Parse lines - support both whitespace and dot-prefix for indentation + // Parse lines - support both whitespace and dash-prefix for indentation const items = lines.map((line, index) => { - const dotMatch = line.match(/^(\.+)/); + const dashMatch = line.match(/^(-+)/); let name: string; let indent: number; let comment: string | undefined; - if (dotMatch) { - name = line.slice(dotMatch[1].length); - indent = dotMatch[1].length; + if (dashMatch) { + name = line.slice(dashMatch[1].length); + indent = dashMatch[1].length; } else { name = line.trim(); indent = line.length - line.trimStart().length; @@ -153,46 +153,56 @@ interface FileRowProps { function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { const isClickable = node.isFolder; + const isDotfile = node.name.startsWith("."); return ( -
- {/* Caret for folders */} - {isClickable ? ( - - ) : ( -
- )} + + + + + ) : ( +
+ )} + + + {node.name} + +
- - {node.name} - + {/* Right side: comment */} {node.comment && ( - {node.comment} + # {node.comment} )}
); diff --git a/pages/index.mdx b/pages/index.mdx index e5bb060..3434ac5 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -73,37 +73,32 @@ Scratch uses an opinionated project structure and requires **no boilerplate or c A simple Scratch project (created with `scratch create`) looks like this: -Use `scratch build` to compile this project into a static website, like this one. You can change styles and customize the page wrapper component by modifying the files in the `src/` directory: - - -Component files and js/ts libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. - -Scratch installs build dependencies automatically. You can add additional third-party dependencies by including a `package.json` file in your project root. +Use `scratch build` to compile this project into a static website, like this one. -## Built on Bun +Component files and libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. -Scratch is built on [Bun](https://bun.com/) for lightning-fast builds, development with HMR, and native typescript support. It uses the [Tailwind CSS](https://tailwindcss.com/) framework to make component styling easy. - -Scratch compiles Javascript (.js), Typescript (.ts), JSX (.jsx), TSX (.tsx), Markdown (.md), and MDX (.mdx). +Modify `src/tailwind.css` directory to change the styling of your document. Add headers, footers and other site-wide elements by modifying `src/PageWrapper.jsx`. ## Commands @@ -133,3 +128,18 @@ scratch revert --list # list available template files # Update scratch to latest version scratch update ``` + +## Acknowledgements + + +Scratch is built on [Bun](https://bun.com/) for lightning-fast builds, development with HMR, and native typescript support. It uses the [Tailwind CSS](https://tailwindcss.com/) framework to make component styling easy. + +[React](https://react.dev/) and [MDX](https://mdxjs.com/) make it possible to write with Markdown and code. + +Content processing is handled by the [unified](https://unifiedjs.com/) ecosystem, with [remark-gfm](https://github.com/remarkjs/remark-gfm) for GitHub Flavored Markdown and [remark-frontmatter](https://github.com/remarkjs/remark-frontmatter) plus [gray-matter](https://github.com/jonschlinkert/gray-matter) for parsing front matter. + +[Shiki](https://shiki.style/) provides beautiful, accurate syntax highlighting with VS Code's grammar engine. + +[Commander.js](https://github.com/tj/commander.js) powers the CLI, and [fast-glob](https://github.com/mrmlnc/fast-glob) handles file discovery. + +Additional dependencies: [acorn](https://github.com/acornjs/acorn), [@mdx-js/esbuild](https://mdxjs.com/packages/esbuild/), [@shikijs/rehype](https://shiki.style/packages/rehype), [@types/mdast](https://github.com/DefinitelyTyped/DefinitelyTyped), [unist-util-visit](https://github.com/syntax-tree/unist-util-visit), [unist-util-is](https://github.com/syntax-tree/unist-util-is). From a6a69ae055a98e14ad1aeecd9e260039d1010cda Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 11:48:30 -0800 Subject: [PATCH 11/40] clean up footer --- src/PageWrapper.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PageWrapper.jsx b/src/PageWrapper.jsx index f39868c..7fcdfe6 100644 --- a/src/PageWrapper.jsx +++ b/src/PageWrapper.jsx @@ -18,9 +18,7 @@ export default function PageWrapper({ children }) {
{children}
- Released under the MIT License -
- Copyright 2025 Pete Koomen + MIT License · © 2025 Pete Koomen
); From 48e545a4da41a2cdd8b0ccd7d318360ce2856cf4 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 14:21:09 -0800 Subject: [PATCH 12/40] Fix file comments on mobile --- pages/Files.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/Files.tsx b/pages/Files.tsx index 630cf98..3773b99 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -202,7 +202,7 @@ function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { {/* Right side: comment */} {node.comment && ( - # {node.comment} + # {node.comment} )}
); @@ -255,7 +255,7 @@ export default function Files({ content, children }: FilesProps) { }; return ( -
+
{nodes.map((node) => ( Date: Wed, 24 Dec 2025 14:32:50 -0800 Subject: [PATCH 13/40] clean up files --- pages/Files.tsx | 6 ++++-- pages/index.mdx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pages/Files.tsx b/pages/Files.tsx index 3773b99..8b04471 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -159,7 +159,7 @@ function FileRow({ node, isCollapsed, onToggle }: FileRowProps) {
{/* Left side: indent + caret + name */}
{/* Caret for folders */} @@ -202,7 +202,9 @@ function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { {/* Right side: comment */} {node.comment && ( - # {node.comment} + + {node.comment} + )}
); diff --git a/pages/index.mdx b/pages/index.mdx index 3434ac5..2ea6dbf 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -94,7 +94,7 @@ my-scratch-project/ `} /> -Use `scratch build` to compile this project into a static website, like this one. +Use `scratch build` to compile this project into a static website, like [this one](https://github.com/scratch/scratch.dev). Component files and libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. From 990b4dc809c84a074e41c1ef1eacde51dd5b5d74 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 16:24:52 -0800 Subject: [PATCH 14/40] tweaks --- pages/BouncingDvdLogo.tsx | 53 ++++++++++++++++++++++++++++++++------- pages/Counter.tsx | 2 +- pages/index.mdx | 24 ++++++++---------- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/pages/BouncingDvdLogo.tsx b/pages/BouncingDvdLogo.tsx index 3f9ebf8..7419cfc 100644 --- a/pages/BouncingDvdLogo.tsx +++ b/pages/BouncingDvdLogo.tsx @@ -4,9 +4,19 @@ export default function BouncingDvdLogo() { const containerRef = useRef(null); const [cornerHits, setCornerHits] = useState(0); const [position, setPosition] = useState({ x: 20, y: 20 }); - const [velocity, setVelocity] = useState({ x: 2, y: 1.5 }); + const [speedMultiplier, setSpeedMultiplier] = useState(1); + const [velocity, setVelocity] = useState({ x: 2 * 1, y: 1.5 * 1 }); const [color, setColor] = useState("#8b5cf6"); + const toggleSpeed = () => { + const newMultiplier = speedMultiplier === 10 ? 1 : 10; + setSpeedMultiplier(newMultiplier); + setVelocity((v) => ({ + x: Math.sign(v.x) * 2 * newMultiplier, + y: Math.sign(v.y) * 1.5 * newMultiplier, + })); + }; + const logoWidth = 80; const logoHeight = 40; @@ -21,13 +31,17 @@ export default function BouncingDvdLogo() { const getRandomColor = () => { const newColor = colors[Math.floor(Math.random() * colors.length)]; - return newColor === color ? colors[(colors.indexOf(color) + 1) % colors.length] : newColor; + return newColor === color + ? colors[(colors.indexOf(color) + 1) % colors.length] + : newColor; }; useEffect(() => { const container = containerRef.current; if (!container) return; + const cornerTolerance = 10; // pixels of tolerance for corner detection + const animate = () => { setPosition((prev) => { const containerWidth = container.clientWidth; @@ -64,8 +78,15 @@ export default function BouncingDvdLogo() { if (!hitX) setColor(getRandomColor()); } - // Corner hit! - if (hitX && hitY) { + // Corner hit - check if near both edges (with tolerance) + const nearLeftOrRight = + newX <= cornerTolerance || + newX >= containerWidth - logoWidth - cornerTolerance; + const nearTopOrBottom = + newY <= cornerTolerance || + newY >= containerHeight - logoHeight - cornerTolerance; + + if ((hitX || hitY) && nearLeftOrRight && nearTopOrBottom) { setCornerHits((c) => c + 1); } @@ -80,7 +101,10 @@ export default function BouncingDvdLogo() { return (
{/* TV outer frame */} -
+
{/* TV screen bezel */}
{/* Screen container */} @@ -88,9 +112,17 @@ export default function BouncingDvdLogo() { ref={containerRef} className="relative w-full h-48 rounded overflow-hidden" style={{ - background: "linear-gradient(145deg, #1a1a2e 0%, #0f0f1a 50%, #1a1a2e 100%)", + background: + "linear-gradient(145deg, #1a1a2e 0%, #0f0f1a 50%, #1a1a2e 100%)", }} > + {/* Corner hits counter in center */} +
+ + {cornerHits} + +
+ {/* Bouncing logo */}
-

- Corner hits: {cornerHits} + {/* Prompt text */} +

+ "Make a component that looks like a TV screen with a bouncing DVD logo. + Count the number of times...

); diff --git a/pages/Counter.tsx b/pages/Counter.tsx index 03186e1..b55604a 100644 --- a/pages/Counter.tsx +++ b/pages/Counter.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState } from "react"; export default function Counter(): React.ReactElement { const [count, setCount] = useState(0); diff --git a/pages/index.mdx b/pages/index.mdx index 2ea6dbf..8cc68f8 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -58,10 +58,6 @@ Scratch also supports Github-flavored Markdown features like checklists and tabl Unlike traditional word processors, Scratch makes it easy to express any idea. If you can describe it to a coding agent, you can add it to your document: -```text -Make me a component that looks like a TV screen with a bouncing DVD logo. Count the number of times the logo hits a corner and display that below the TV. -``` - Collaborating with AI makes writing more fun. Scratch makes that easy. @@ -88,8 +84,8 @@ my-scratch-project/ ---CodeBlock.tsx ---Heading.tsx ---Link.tsx --AGENTS.md # context for coding agents --package.json # build and additional dependencies +-AGENTS.md # agent context +-package.json # dependencies -.gitignore `} /> @@ -121,9 +117,9 @@ scratch preview scratch clean # Revert a file to its template version -scratch revert [file] # revert a file to its template version -scratch revert --force [file] # overwrite without confirmation -scratch revert --list # list available template files +scratch get [file] # revert a file to its template version +scratch get --force [file] # overwrite without confirmation +scratch get --list # list available template files # Update scratch to latest version scratch update @@ -132,14 +128,14 @@ scratch update ## Acknowledgements -Scratch is built on [Bun](https://bun.com/) for lightning-fast builds, development with HMR, and native typescript support. It uses the [Tailwind CSS](https://tailwindcss.com/) framework to make component styling easy. +[Bun](https://bun.com/) for lightning-fast builds, development with HMR, native typescript support, and a portable executable. -[React](https://react.dev/) and [MDX](https://mdxjs.com/) make it possible to write with Markdown and code. +[React](https://react.dev/) and [MDX](https://mdxjs.com/) make it possible to write with Markdown and code. [Tailwind CSS](https://tailwindcss.com/) makes component styling easy. -Content processing is handled by the [unified](https://unifiedjs.com/) ecosystem, with [remark-gfm](https://github.com/remarkjs/remark-gfm) for GitHub Flavored Markdown and [remark-frontmatter](https://github.com/remarkjs/remark-frontmatter) plus [gray-matter](https://github.com/jonschlinkert/gray-matter) for parsing front matter. +Content preprocessing relies on [unified](https://unifiedjs.com/), with [remark-gfm](https://github.com/remarkjs/remark-gfm) for GitHub Flavored Markdown and [remark-frontmatter](https://github.com/remarkjs/remark-frontmatter) plus [gray-matter](https://github.com/jonschlinkert/gray-matter) for parsing front matter. [Shiki](https://shiki.style/) provides beautiful, accurate syntax highlighting with VS Code's grammar engine. -[Commander.js](https://github.com/tj/commander.js) powers the CLI, and [fast-glob](https://github.com/mrmlnc/fast-glob) handles file discovery. +[Commander.js](https://github.com/tj/commander.js) scaffolds the CLI. -Additional dependencies: [acorn](https://github.com/acornjs/acorn), [@mdx-js/esbuild](https://mdxjs.com/packages/esbuild/), [@shikijs/rehype](https://shiki.style/packages/rehype), [@types/mdast](https://github.com/DefinitelyTyped/DefinitelyTyped), [unist-util-visit](https://github.com/syntax-tree/unist-util-visit), [unist-util-is](https://github.com/syntax-tree/unist-util-is). +Additional dependencies: [fast-glob](https://github.com/mrmlnc/fast-glob), [acorn](https://github.com/acornjs/acorn), [@mdx-js/esbuild](https://mdxjs.com/packages/esbuild/), [@shikijs/rehype](https://shiki.style/packages/rehype), [@types/mdast](https://github.com/DefinitelyTyped/DefinitelyTyped), [unist-util-visit](https://github.com/syntax-tree/unist-util-visit), [unist-util-is](https://github.com/syntax-tree/unist-util-is). From 19ab9badd2fa80a6e1f2d6c89eb14963837c442f Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 21:25:53 -0800 Subject: [PATCH 15/40] update tailwind --- src/tailwind.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tailwind.css b/src/tailwind.css index a31aad8..db0ab5b 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -44,9 +44,9 @@ @apply mr-2 align-middle ml-1; } -/* Tables - add horizontal padding, prevent overflow */ +/* Tables - center by default, prevent overflow */ .prose :where(table):not(:where([class~='not-prose'] *)) { - @apply mx-4 w-auto max-w-full; + @apply mx-auto w-auto max-w-full; } /* From 04c63bcb21cb4a6b034ca9d916073a44b3cdf5d2 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Wed, 24 Dec 2025 22:12:36 -0800 Subject: [PATCH 16/40] some animation on Files component --- AGENTS.md | 126 +++++++++++++++++---- pages/Files.tsx | 285 ++++++++++++++++++++++++++++++++++++++++++++++- pages/index.mdx | 4 +- src/tailwind.css | 50 +++++++++ 4 files changed, 433 insertions(+), 32 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5b5c85f..152a2ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,13 +29,16 @@ Run `scratch --help` to see all available commands. project/ ├── pages/ # MDX and Markdown content (required) │ ├── index.mdx # Homepage (resolves to /) +│ ├── Counter.tsx # Components can live alongside pages │ └── posts/ │ └── hello.mdx # Resolves to /posts/hello/ -├── components/ # React components (optional) -│ └── Button.jsx +├── src/ # React components and styles (optional) +│ ├── Button.jsx +│ ├── PageWrapper.jsx +│ ├── tailwind.css +│ └── markdown/ # Custom markdown renderers ├── public/ # Static assets (optional, copied as-is) │ └── logo.png -├── tailwind.css # Tailwind theme customization (optional) └── dist/ # Build output (generated) ``` @@ -79,7 +82,7 @@ The pattern: `index.mdx` resolves to its parent directory path, other files get ### Auto-Import (No Explicit Imports Needed!) -Components in `components/` or `pages/` are **automatically available** in MDX files without importing them. Just use them: +Components in `src/` or `pages/` are **automatically available** in MDX files without importing them. Just use them: ```mdx # My Page @@ -92,19 +95,53 @@ Components in `components/` or `pages/` are **automatically available** in MDX f The build automatically injects the necessary imports. **Important:** The component name must match the filename: -- `components/Button.jsx` → ` +important text +``` + +**Block children** (markdown with blank lines): +```mdx + + +## Warning Title + +This is a **markdown** paragraph inside the component. + +- List item one +- List item two + + +``` + +The blank lines after the opening tag and before the closing tag are required for block-level markdown to be parsed correctly. ### Styling with Tailwind Components can use Tailwind CSS utility classes - they're globally available: ```jsx -// components/Card.jsx +// src/Card.jsx export function Card({ children }) { return (
@@ -116,10 +153,10 @@ export function Card({ children }) { ### PageWrapper Component -If you create a `components/PageWrapper.jsx`, it will **automatically wrap all page content**. Useful for layouts: +If you create a `src/PageWrapper.jsx`, it will **automatically wrap all page content**. Useful for layouts: ```jsx -// components/PageWrapper.jsx +// src/PageWrapper.jsx export default function PageWrapper({ children }) { return (
@@ -133,7 +170,7 @@ export default function PageWrapper({ children }) { ### Markdown Components -Components in `components/markdown/` override default Markdown element rendering: +Components in `src/markdown/` override default Markdown element rendering: - `Heading.tsx` - Custom heading rendering (h1-h6) - `CodeBlock.tsx` - Custom code block rendering with syntax highlighting @@ -148,25 +185,66 @@ Files in `public/` are copied directly to the build output. Reference them with ## Theming -Scratch uses custom prose styling defined in `tailwind.css` for markdown content. The default template includes: +Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography) for markdown styling. The `prose` class is applied via PageWrapper. + +### Customizing Typography + +- **Size variants**: Add `prose-sm`, `prose-lg`, `prose-xl` in PageWrapper.jsx +- **Color themes**: Add `prose-slate`, `prose-zinc`, `prose-neutral`, etc. -- `scratch-prose` class for typography styling -- Dark mode support (follows system preference via `.dark` class) +### Overriding Prose Styling (No Custom Component) -### Customizing the Theme +Tailwind Typography supports element modifiers to override styling for specific element types directly in `PageWrapper.jsx`: -The `tailwind.css` file contains all prose styling for markdown elements. You can customize: +```jsx +
+``` -- Headings (h1-h4), paragraphs, links, lists -- Code blocks and inline code -- Blockquotes, tables, images -- Light and dark mode colors +Available element modifiers: +- `prose-headings:` - all headings (h1-h6) +- `prose-h1:`, `prose-h2:`, etc. - specific heading levels +- `prose-a:` - links +- `prose-p:` - paragraphs +- `prose-blockquote:` - blockquotes +- `prose-code:` - inline code +- `prose-pre:` - code blocks +- `prose-ol:`, `prose-ul:`, `prose-li:` - lists +- `prose-table:`, `prose-th:`, `prose-td:` - tables +- `prose-img:`, `prose-figure:`, `prose-figcaption:` - images + +You can also add CSS overrides in `src/tailwind.css`: +```css +.prose a { + @apply text-blue-600 hover:text-blue-800 no-underline; +} +``` -Simply edit the `.scratch-prose` rules in `tailwind.css` to match your design. +### Overriding with Custom Components -### Dark Mode +For more complex overrides (adding interactivity, conditional logic), create a custom component in `src/markdown/`: + +1. Create/edit a component (e.g., `Link.tsx`) +2. Export from `src/markdown/index.ts` and add to `MDXComponents` + +Example: +```tsx +// src/markdown/Link.tsx +export default function Link({ href, children, ...props }) { + const isExternal = href?.startsWith('http'); + return ( + + {children} + + ); +} +``` -Dark mode is enabled by default and follows system preferences. The `PageWrapper` component uses the `scratch-prose` class, and dark mode styles are automatically applied when the `.dark` class is present on a parent element. +Use `not-prose` class to exclude elements from typography styling. ## Generated Files diff --git a/pages/Files.tsx b/pages/Files.tsx index 8b04471..eb1121c 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useRef, useEffect } from "react"; // Tree node structure from parsing interface TreeNode { @@ -145,18 +145,46 @@ function getInitialCollapsed(roots: TreeNode[]): Set { return collapsed; } +const ROW_HEIGHT = 28; // h-7 = 1.75rem = 28px + interface FileRowProps { node: RenderNode; isCollapsed: boolean; onToggle: () => void; + animationState?: "entering" | "exiting" | "none"; + slideOffset?: number; // pixels to translate Y + isSliding?: boolean; + isHidden?: boolean; } -function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { +function FileRow({ + node, + isCollapsed, + onToggle, + animationState = "none", + slideOffset = 0, + isSliding = false, + isHidden = false, +}: FileRowProps) { const isClickable = node.isFolder; const isDotfile = node.name.startsWith("."); + const classes = ["flex", "items-center", "h-7", "font-mono", "text-sm"]; + + if (animationState === "entering") classes.push("animate-fade-in"); + if (animationState === "exiting") classes.push("animate-fade-out"); + if (isSliding) classes.push("animate-slide"); + + const style: React.CSSProperties = {}; + if (isSliding && slideOffset !== 0) { + (style as Record)["--slide-offset"] = `${slideOffset}px`; + } + if (isHidden) { + style.opacity = 0; + } + return ( -
+
{/* Left side: indent + caret + name */}
@@ -235,6 +263,31 @@ interface FilesProps { children?: React.ReactNode; } +const ANIMATION_DURATION = 1000; // ms + +type AnimationPhase = + | "idle" + | "expanding-slide" // items below sliding down + | "expanding-fade" // new items fading in + | "collapsing-fade" // items fading out + | "collapsing-slide"; // items below sliding up + +interface AnimationInfo { + phase: AnimationPhase; + toggledFolderId: string | null; + enteringIds: Set; + exitingNodes: RenderNode[]; + itemCount: number; // number of items entering or exiting +} + +const IDLE_ANIMATION: AnimationInfo = { + phase: "idle", + toggledFolderId: null, + enteringIds: new Set(), + exitingNodes: [], + itemCount: 0, +}; + export default function Files({ content, children }: FilesProps) { const text = content ?? extractText(children); const [tree] = useState(() => parseTree(text)); @@ -242,9 +295,133 @@ export default function Files({ content, children }: FilesProps) { getInitialCollapsed(tree) ); - const nodes = flattenTree(tree, collapsedIds); + // Force re-render trigger + const [, forceUpdate] = useState(0); + + // Store animation state in ref for synchronous access during render + const animRef = useRef(IDLE_ANIMATION); + + // Track previous state for detecting changes + const prevStateRef = useRef<{ + collapsedIds: Set; + visibleIds: Set; + }>({ + collapsedIds: new Set(collapsedIds), + visibleIds: new Set(), + }); + + const currentNodes = flattenTree(tree, collapsedIds); + const currentVisibleIds = new Set(currentNodes.map((n) => n.id)); + + // Initialize prev visible IDs on first render + if (prevStateRef.current.visibleIds.size === 0) { + prevStateRef.current.visibleIds = currentVisibleIds; + } + + // Synchronously detect toggle and set up animation during render + const prevCollapsed = prevStateRef.current.collapsedIds; + const prevVisibleIds = prevStateRef.current.visibleIds; + + let toggledFolder: { id: string; expanded: boolean } | null = null; + for (const id of collapsedIds) { + if (!prevCollapsed.has(id)) { + toggledFolder = { id, expanded: false }; + break; + } + } + if (!toggledFolder) { + for (const id of prevCollapsed) { + if (!collapsedIds.has(id)) { + toggledFolder = { id, expanded: true }; + break; + } + } + } + + // If a toggle just happened and we're idle, start the animation synchronously + if (toggledFolder && animRef.current.phase === "idle") { + const allNodesFlat = flattenTree(tree, new Set()); + + if (toggledFolder.expanded) { + // Expanding: find entering nodes + const entering = new Set(); + for (const id of currentVisibleIds) { + if (!prevVisibleIds.has(id)) { + entering.add(id); + } + } + + if (entering.size > 0) { + animRef.current = { + phase: "expanding-slide", + toggledFolderId: toggledFolder.id, + enteringIds: entering, + exitingNodes: [], + itemCount: entering.size, + }; + } + } else { + // Collapsing: find exiting nodes + const exiting: RenderNode[] = []; + for (const id of prevVisibleIds) { + if (!currentVisibleIds.has(id)) { + const node = allNodesFlat.find((n) => n.id === id); + if (node) exiting.push(node); + } + } + + if (exiting.length > 0) { + animRef.current = { + phase: "collapsing-fade", + toggledFolderId: toggledFolder.id, + enteringIds: new Set(), + exitingNodes: exiting, + itemCount: exiting.length, + }; + } + } + + // Update prev state + prevStateRef.current = { + collapsedIds: new Set(collapsedIds), + visibleIds: currentVisibleIds, + }; + } + + // Get current animation state from ref + const animation = animRef.current; + + // Transition between animation phases using effect + useEffect(() => { + if (animRef.current.phase === "idle") return; + + const timer = setTimeout(() => { + switch (animRef.current.phase) { + case "expanding-slide": + animRef.current = { ...animRef.current, phase: "expanding-fade" }; + forceUpdate((n) => n + 1); + break; + case "expanding-fade": + animRef.current = IDLE_ANIMATION; + forceUpdate((n) => n + 1); + break; + case "collapsing-fade": + animRef.current = { ...animRef.current, phase: "collapsing-slide" }; + forceUpdate((n) => n + 1); + break; + case "collapsing-slide": + animRef.current = IDLE_ANIMATION; + forceUpdate((n) => n + 1); + break; + } + }, ANIMATION_DURATION); + + return () => clearTimeout(timer); + }, [animation.phase]); const toggleCollapse = (id: string) => { + if (animRef.current.phase !== "idle") return; // Don't allow toggling during animation + setCollapsedIds((prev) => { const next = new Set(prev); if (next.has(id)) { @@ -256,14 +433,110 @@ export default function Files({ content, children }: FilesProps) { }); }; + // Build the list of nodes to render + const { phase, toggledFolderId, enteringIds, exitingNodes, itemCount } = + animation; + + // For collapsing, we need to include exiting nodes in the render + const allNodes = [...currentNodes]; + + if (phase === "collapsing-fade") { + // Insert exiting nodes after the toggled folder (only during fade, not slide) + const folderIndex = allNodes.findIndex((n) => n.id === toggledFolderId); + if (folderIndex !== -1) { + allNodes.splice(folderIndex + 1, 0, ...exitingNodes); + } + } + + // Find the index of the toggled folder to determine which items slide + const toggledFolderIndex = allNodes.findIndex( + (n) => n.id === toggledFolderId + ); + + const getAnimationState = ( + nodeId: string + ): "entering" | "exiting" | "none" => { + if (phase === "expanding-fade" && enteringIds.has(nodeId)) { + return "entering"; + } + if ( + phase === "collapsing-fade" && + exitingNodes.some((n) => n.id === nodeId) + ) { + return "exiting"; + } + return "none"; + }; + + const getSlideOffset = (index: number): number => { + if (toggledFolderIndex === -1) return 0; + + // For expanding-slide: items below the new content need to slide down first + // The entering items are already in the list, so items after them slide + if (phase === "expanding-slide") { + // Find the first entering item's index + const firstEnteringIndex = allNodes.findIndex((n) => + enteringIds.has(n.id) + ); + if (firstEnteringIndex !== -1 && index >= firstEnteringIndex) { + // Items at or after the first entering item + if (enteringIds.has(allNodes[index].id)) { + // Entering items start invisible (handled by opacity) + return 0; + } + // Items below slide down from above (start offset, will animate to 0) + return -itemCount * ROW_HEIGHT; + } + } + + // For collapsing-slide: items below the folder slide up from their old positions + // Exiting nodes are no longer in the DOM, so items are at final positions + // We offset them down (positive) to their old visual positions, then animate to 0 + if (phase === "collapsing-slide") { + if (index > toggledFolderIndex) { + return itemCount * ROW_HEIGHT; + } + } + + return 0; + }; + + const isSliding = (index: number): boolean => { + if (toggledFolderIndex === -1) return false; + + if (phase === "expanding-slide") { + const firstEnteringIndex = allNodes.findIndex((n) => + enteringIds.has(n.id) + ); + if (firstEnteringIndex !== -1 && index >= firstEnteringIndex) { + return !enteringIds.has(allNodes[index].id); + } + } + + if (phase === "collapsing-slide") { + return index > toggledFolderIndex; + } + + return false; + }; + + // Hide entering items during slide phase (before they fade in) + const isHidden = (nodeId: string): boolean => { + return phase === "expanding-slide" && enteringIds.has(nodeId); + }; + return (
- {nodes.map((node) => ( + {allNodes.map((node, index) => ( toggleCollapse(node.id)} + animationState={getAnimationState(node.id)} + slideOffset={getSlideOffset(index)} + isSliding={isSliding(index)} + isHidden={isHidden(node.id)} /> ))}
diff --git a/pages/index.mdx b/pages/index.mdx index 8cc68f8..20470d4 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -100,14 +100,14 @@ Modify `src/tailwind.css` directory to change the styling of your document. Add ```bash # Create a new project -scratch create [path] # create project at path (default: current directory) +scratch create [path] # create project # Start dev server with hot module reloading scratch dev # Build for production scratch build -scratch build --no-ssg # disable static site generation +scratch build --no-ssg # disable server-side generation scratch build --development # unminified, with source maps # Preview production build locally diff --git a/src/tailwind.css b/src/tailwind.css index db0ab5b..f5f6bb5 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -72,3 +72,53 @@ pre > code { @apply absolute -left-6 top-0 opacity-0 group-hover:opacity-100 transition-opacity no-underline select-none; @apply text-gray-400 hover:text-gray-600; } + +/* Files component animations */ +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.animate-fade-in { + animation: fade-in 1000ms ease-out forwards; +} + +.animate-fade-out { + animation: fade-out 1000ms ease-out forwards; +} + +/* Slide animations use CSS custom properties for dynamic offset */ +@keyframes slide-down { + from { + transform: translateY(var(--slide-offset)); + } + to { + transform: translateY(0); + } +} + +@keyframes slide-up { + from { + transform: translateY(var(--slide-offset)); + } + to { + transform: translateY(0); + } +} + +.animate-slide { + animation: slide-down 1000ms ease-out forwards; +} From 4c2e167f4579aa18f299ccfdd7889fdc57364b4e Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Thu, 25 Dec 2025 11:53:56 -0800 Subject: [PATCH 17/40] simplify file animations --- pages/Files.tsx | 283 +---------------------------------------------- src/tailwind.css | 50 --------- 2 files changed, 5 insertions(+), 328 deletions(-) diff --git a/pages/Files.tsx b/pages/Files.tsx index eb1121c..48ba629 100644 --- a/pages/Files.tsx +++ b/pages/Files.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState } from "react"; // Tree node structure from parsing interface TreeNode { @@ -145,46 +145,18 @@ function getInitialCollapsed(roots: TreeNode[]): Set { return collapsed; } -const ROW_HEIGHT = 28; // h-7 = 1.75rem = 28px - interface FileRowProps { node: RenderNode; isCollapsed: boolean; onToggle: () => void; - animationState?: "entering" | "exiting" | "none"; - slideOffset?: number; // pixels to translate Y - isSliding?: boolean; - isHidden?: boolean; } -function FileRow({ - node, - isCollapsed, - onToggle, - animationState = "none", - slideOffset = 0, - isSliding = false, - isHidden = false, -}: FileRowProps) { +function FileRow({ node, isCollapsed, onToggle }: FileRowProps) { const isClickable = node.isFolder; const isDotfile = node.name.startsWith("."); - const classes = ["flex", "items-center", "h-7", "font-mono", "text-sm"]; - - if (animationState === "entering") classes.push("animate-fade-in"); - if (animationState === "exiting") classes.push("animate-fade-out"); - if (isSliding) classes.push("animate-slide"); - - const style: React.CSSProperties = {}; - if (isSliding && slideOffset !== 0) { - (style as Record)["--slide-offset"] = `${slideOffset}px`; - } - if (isHidden) { - style.opacity = 0; - } - return ( -
+
{/* Left side: indent + caret + name */}
; - exitingNodes: RenderNode[]; - itemCount: number; // number of items entering or exiting -} - -const IDLE_ANIMATION: AnimationInfo = { - phase: "idle", - toggledFolderId: null, - enteringIds: new Set(), - exitingNodes: [], - itemCount: 0, -}; - export default function Files({ content, children }: FilesProps) { const text = content ?? extractText(children); const [tree] = useState(() => parseTree(text)); @@ -295,133 +242,9 @@ export default function Files({ content, children }: FilesProps) { getInitialCollapsed(tree) ); - // Force re-render trigger - const [, forceUpdate] = useState(0); - - // Store animation state in ref for synchronous access during render - const animRef = useRef(IDLE_ANIMATION); - - // Track previous state for detecting changes - const prevStateRef = useRef<{ - collapsedIds: Set; - visibleIds: Set; - }>({ - collapsedIds: new Set(collapsedIds), - visibleIds: new Set(), - }); - - const currentNodes = flattenTree(tree, collapsedIds); - const currentVisibleIds = new Set(currentNodes.map((n) => n.id)); - - // Initialize prev visible IDs on first render - if (prevStateRef.current.visibleIds.size === 0) { - prevStateRef.current.visibleIds = currentVisibleIds; - } - - // Synchronously detect toggle and set up animation during render - const prevCollapsed = prevStateRef.current.collapsedIds; - const prevVisibleIds = prevStateRef.current.visibleIds; - - let toggledFolder: { id: string; expanded: boolean } | null = null; - for (const id of collapsedIds) { - if (!prevCollapsed.has(id)) { - toggledFolder = { id, expanded: false }; - break; - } - } - if (!toggledFolder) { - for (const id of prevCollapsed) { - if (!collapsedIds.has(id)) { - toggledFolder = { id, expanded: true }; - break; - } - } - } - - // If a toggle just happened and we're idle, start the animation synchronously - if (toggledFolder && animRef.current.phase === "idle") { - const allNodesFlat = flattenTree(tree, new Set()); - - if (toggledFolder.expanded) { - // Expanding: find entering nodes - const entering = new Set(); - for (const id of currentVisibleIds) { - if (!prevVisibleIds.has(id)) { - entering.add(id); - } - } - - if (entering.size > 0) { - animRef.current = { - phase: "expanding-slide", - toggledFolderId: toggledFolder.id, - enteringIds: entering, - exitingNodes: [], - itemCount: entering.size, - }; - } - } else { - // Collapsing: find exiting nodes - const exiting: RenderNode[] = []; - for (const id of prevVisibleIds) { - if (!currentVisibleIds.has(id)) { - const node = allNodesFlat.find((n) => n.id === id); - if (node) exiting.push(node); - } - } - - if (exiting.length > 0) { - animRef.current = { - phase: "collapsing-fade", - toggledFolderId: toggledFolder.id, - enteringIds: new Set(), - exitingNodes: exiting, - itemCount: exiting.length, - }; - } - } - - // Update prev state - prevStateRef.current = { - collapsedIds: new Set(collapsedIds), - visibleIds: currentVisibleIds, - }; - } - - // Get current animation state from ref - const animation = animRef.current; - - // Transition between animation phases using effect - useEffect(() => { - if (animRef.current.phase === "idle") return; - - const timer = setTimeout(() => { - switch (animRef.current.phase) { - case "expanding-slide": - animRef.current = { ...animRef.current, phase: "expanding-fade" }; - forceUpdate((n) => n + 1); - break; - case "expanding-fade": - animRef.current = IDLE_ANIMATION; - forceUpdate((n) => n + 1); - break; - case "collapsing-fade": - animRef.current = { ...animRef.current, phase: "collapsing-slide" }; - forceUpdate((n) => n + 1); - break; - case "collapsing-slide": - animRef.current = IDLE_ANIMATION; - forceUpdate((n) => n + 1); - break; - } - }, ANIMATION_DURATION); - - return () => clearTimeout(timer); - }, [animation.phase]); + const nodes = flattenTree(tree, collapsedIds); const toggleCollapse = (id: string) => { - if (animRef.current.phase !== "idle") return; // Don't allow toggling during animation - setCollapsedIds((prev) => { const next = new Set(prev); if (next.has(id)) { @@ -433,110 +256,14 @@ export default function Files({ content, children }: FilesProps) { }); }; - // Build the list of nodes to render - const { phase, toggledFolderId, enteringIds, exitingNodes, itemCount } = - animation; - - // For collapsing, we need to include exiting nodes in the render - const allNodes = [...currentNodes]; - - if (phase === "collapsing-fade") { - // Insert exiting nodes after the toggled folder (only during fade, not slide) - const folderIndex = allNodes.findIndex((n) => n.id === toggledFolderId); - if (folderIndex !== -1) { - allNodes.splice(folderIndex + 1, 0, ...exitingNodes); - } - } - - // Find the index of the toggled folder to determine which items slide - const toggledFolderIndex = allNodes.findIndex( - (n) => n.id === toggledFolderId - ); - - const getAnimationState = ( - nodeId: string - ): "entering" | "exiting" | "none" => { - if (phase === "expanding-fade" && enteringIds.has(nodeId)) { - return "entering"; - } - if ( - phase === "collapsing-fade" && - exitingNodes.some((n) => n.id === nodeId) - ) { - return "exiting"; - } - return "none"; - }; - - const getSlideOffset = (index: number): number => { - if (toggledFolderIndex === -1) return 0; - - // For expanding-slide: items below the new content need to slide down first - // The entering items are already in the list, so items after them slide - if (phase === "expanding-slide") { - // Find the first entering item's index - const firstEnteringIndex = allNodes.findIndex((n) => - enteringIds.has(n.id) - ); - if (firstEnteringIndex !== -1 && index >= firstEnteringIndex) { - // Items at or after the first entering item - if (enteringIds.has(allNodes[index].id)) { - // Entering items start invisible (handled by opacity) - return 0; - } - // Items below slide down from above (start offset, will animate to 0) - return -itemCount * ROW_HEIGHT; - } - } - - // For collapsing-slide: items below the folder slide up from their old positions - // Exiting nodes are no longer in the DOM, so items are at final positions - // We offset them down (positive) to their old visual positions, then animate to 0 - if (phase === "collapsing-slide") { - if (index > toggledFolderIndex) { - return itemCount * ROW_HEIGHT; - } - } - - return 0; - }; - - const isSliding = (index: number): boolean => { - if (toggledFolderIndex === -1) return false; - - if (phase === "expanding-slide") { - const firstEnteringIndex = allNodes.findIndex((n) => - enteringIds.has(n.id) - ); - if (firstEnteringIndex !== -1 && index >= firstEnteringIndex) { - return !enteringIds.has(allNodes[index].id); - } - } - - if (phase === "collapsing-slide") { - return index > toggledFolderIndex; - } - - return false; - }; - - // Hide entering items during slide phase (before they fade in) - const isHidden = (nodeId: string): boolean => { - return phase === "expanding-slide" && enteringIds.has(nodeId); - }; - return (
- {allNodes.map((node, index) => ( + {nodes.map((node) => ( toggleCollapse(node.id)} - animationState={getAnimationState(node.id)} - slideOffset={getSlideOffset(index)} - isSliding={isSliding(index)} - isHidden={isHidden(node.id)} /> ))}
diff --git a/src/tailwind.css b/src/tailwind.css index f5f6bb5..db0ab5b 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -72,53 +72,3 @@ pre > code { @apply absolute -left-6 top-0 opacity-0 group-hover:opacity-100 transition-opacity no-underline select-none; @apply text-gray-400 hover:text-gray-600; } - -/* Files component animations */ -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes fade-out { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -.animate-fade-in { - animation: fade-in 1000ms ease-out forwards; -} - -.animate-fade-out { - animation: fade-out 1000ms ease-out forwards; -} - -/* Slide animations use CSS custom properties for dynamic offset */ -@keyframes slide-down { - from { - transform: translateY(var(--slide-offset)); - } - to { - transform: translateY(0); - } -} - -@keyframes slide-up { - from { - transform: translateY(var(--slide-offset)); - } - to { - transform: translateY(0); - } -} - -.animate-slide { - animation: slide-down 1000ms ease-out forwards; -} From 44b5dcdda7b891717d89ab77b92c57525a404e1f Mon Sep 17 00:00:00 2001 From: Pete Koomen Date: Fri, 26 Dec 2025 00:50:06 -0800 Subject: [PATCH 18/40] move components into their own directory --- pages/{ => components}/BouncingDvdLogo.tsx | 4 ++-- pages/{ => components}/Counter.tsx | 0 {public => pages/components}/DVD_logo.svg | 0 pages/{ => components}/Files.tsx | 0 pages/{ => components}/Fire.tsx | 0 pages/{ => components}/TodoList.tsx | 0 6 files changed, 2 insertions(+), 2 deletions(-) rename pages/{ => components}/BouncingDvdLogo.tsx (97%) rename pages/{ => components}/Counter.tsx (100%) rename {public => pages/components}/DVD_logo.svg (100%) rename pages/{ => components}/Files.tsx (100%) rename pages/{ => components}/Fire.tsx (100%) rename pages/{ => components}/TodoList.tsx (100%) diff --git a/pages/BouncingDvdLogo.tsx b/pages/components/BouncingDvdLogo.tsx similarity index 97% rename from pages/BouncingDvdLogo.tsx rename to pages/components/BouncingDvdLogo.tsx index 7419cfc..7938642 100644 --- a/pages/BouncingDvdLogo.tsx +++ b/pages/components/BouncingDvdLogo.tsx @@ -139,11 +139,11 @@ export default function BouncingDvdLogo() { width: "100%", height: "100%", backgroundColor: color, - maskImage: "url(/DVD_logo.svg)", + maskImage: "url(/components/DVD_logo.svg)", maskSize: "contain", maskRepeat: "no-repeat", maskPosition: "center", - WebkitMaskImage: "url(/DVD_logo.svg)", + WebkitMaskImage: "url(/components/DVD_logo.svg)", WebkitMaskSize: "contain", WebkitMaskRepeat: "no-repeat", WebkitMaskPosition: "center", diff --git a/pages/Counter.tsx b/pages/components/Counter.tsx similarity index 100% rename from pages/Counter.tsx rename to pages/components/Counter.tsx diff --git a/public/DVD_logo.svg b/pages/components/DVD_logo.svg similarity index 100% rename from public/DVD_logo.svg rename to pages/components/DVD_logo.svg diff --git a/pages/Files.tsx b/pages/components/Files.tsx similarity index 100% rename from pages/Files.tsx rename to pages/components/Files.tsx diff --git a/pages/Fire.tsx b/pages/components/Fire.tsx similarity index 100% rename from pages/Fire.tsx rename to pages/components/Fire.tsx diff --git a/pages/TodoList.tsx b/pages/components/TodoList.tsx similarity index 100% rename from pages/TodoList.tsx rename to pages/components/TodoList.tsx From d615f45b9f9004f869498576701843353241bbe0 Mon Sep 17 00:00:00 2001 From: Pete Koomen Date: Fri, 26 Dec 2025 00:53:32 -0800 Subject: [PATCH 19/40] add view command to index.mdx --- pages/index.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pages/index.mdx b/pages/index.mdx index 20470d4..2724813 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -107,19 +107,20 @@ scratch dev # Build for production scratch build -scratch build --no-ssg # disable server-side generation -scratch build --development # unminified, with source maps # Preview production build locally scratch preview +# Serve target file/directory on dev server +scratch view path + # Remove build artifacts scratch clean # Revert a file to its template version -scratch get [file] # revert a file to its template version -scratch get --force [file] # overwrite without confirmation -scratch get --list # list available template files +scratch checkout [file] # revert a file to its template version +scratch checkout --force [file] # overwrite without confirmation +scratch checkout --list # list available template files # Update scratch to latest version scratch update From 26b291ab112184796c350ab364becf9cab5436a4 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Thu, 25 Dec 2025 11:56:17 -0800 Subject: [PATCH 20/40] make it clear that dvd logo component is clickable --- pages/components/BouncingDvdLogo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/components/BouncingDvdLogo.tsx b/pages/components/BouncingDvdLogo.tsx index 7938642..ec3cf37 100644 --- a/pages/components/BouncingDvdLogo.tsx +++ b/pages/components/BouncingDvdLogo.tsx @@ -125,7 +125,7 @@ export default function BouncingDvdLogo() { {/* Bouncing logo */}
Date: Sun, 28 Dec 2025 00:35:58 -0800 Subject: [PATCH 21/40] new styling, header and footer --- pages/about.mdx | 6 + pages/docs.mdx | 6 + pages/index.mdx | 11 +- public/scratch.svg | 213 ++++++++++++++++++++++++++++++++ src/Footer.jsx | 12 ++ src/Header.jsx | 21 ++++ src/PageWrapper.jsx | 15 +-- src/ScratchBadge.jsx | 13 ++ src/{tailwind.css => index.css} | 10 ++ 9 files changed, 293 insertions(+), 14 deletions(-) create mode 100644 pages/about.mdx create mode 100644 pages/docs.mdx create mode 100644 public/scratch.svg create mode 100644 src/Footer.jsx create mode 100644 src/Header.jsx create mode 100644 src/ScratchBadge.jsx rename src/{tailwind.css => index.css} (88%) diff --git a/pages/about.mdx b/pages/about.mdx new file mode 100644 index 0000000..1e3319e --- /dev/null +++ b/pages/about.mdx @@ -0,0 +1,6 @@ +--- +title: About +description: About Scratch +--- + +# About diff --git a/pages/docs.mdx b/pages/docs.mdx new file mode 100644 index 0000000..5076ad8 --- /dev/null +++ b/pages/docs.mdx @@ -0,0 +1,6 @@ +--- +title: Docs +description: Documentation for Scratch +--- + +# Documentation diff --git a/pages/index.mdx b/pages/index.mdx index 2724813..390270a 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -1,17 +1,22 @@ --- title: "Scratch" -description: "A CLI for building static MDX websites with Bun" +description: "Write with Markdown and React" keywords: ["MDX", "static site", "React", "Bun", "markdown"] author: "Scratch" type: "website" lang: "en" --- -
+
Scratch
-Scratch compiles Markdown and React into beautiful static websites that can be hosted anywhere. +Scratch is a tool for writing with [Markdown](https://daringfireball.net/projects/markdown/) and [React](https://react.dev/). + +Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites like this one. + +Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask a coding agent for help when it's easier to express yourself with code. + ## Quick Start diff --git a/public/scratch.svg b/public/scratch.svg new file mode 100644 index 0000000..afaa536 --- /dev/null +++ b/public/scratch.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Footer.jsx b/src/Footer.jsx new file mode 100644 index 0000000..84748da --- /dev/null +++ b/src/Footer.jsx @@ -0,0 +1,12 @@ +import ScratchBadge from './ScratchBadge'; + +export default function Footer() { + return ( +
+ +

+ MIT License · © 2025 Pete Koomen +

+
+ ); +} diff --git a/src/Header.jsx b/src/Header.jsx new file mode 100644 index 0000000..f081cf3 --- /dev/null +++ b/src/Header.jsx @@ -0,0 +1,21 @@ +export default function Header() { + return ( +
+ + Scratch + + + +
+ ); +} diff --git a/src/PageWrapper.jsx b/src/PageWrapper.jsx index 7fcdfe6..40df948 100644 --- a/src/PageWrapper.jsx +++ b/src/PageWrapper.jsx @@ -1,4 +1,6 @@ import React from 'react'; +import Header from './Header'; +import Footer from './Footer'; /** * A simple wrapper applied to every page in the demo project. Feel free to @@ -8,18 +10,9 @@ import React from 'react'; export default function PageWrapper({ children }) { return (
- +
{children} -
- MIT License · © 2025 Pete Koomen -
+
); } diff --git a/src/ScratchBadge.jsx b/src/ScratchBadge.jsx new file mode 100644 index 0000000..e78c853 --- /dev/null +++ b/src/ScratchBadge.jsx @@ -0,0 +1,13 @@ +export default function ScratchBadge() { + return ( + + Made from + Scratch + + ); +} diff --git a/src/tailwind.css b/src/index.css similarity index 88% rename from src/tailwind.css rename to src/index.css index db0ab5b..3789800 100644 --- a/src/tailwind.css +++ b/src/index.css @@ -11,6 +11,16 @@ * Custom prose overrides for elements not fully styled by @tailwindcss/typography */ +/* Links - only underline on hover */ +.prose :where(a):not(:where([class~='not-prose'] *)) { + @apply no-underline hover:underline; +} + +/* Headings - add top margin and center */ +.prose :where(h1, h2, h3, h4, h5, h6):not(:where([class~='not-prose'] *)) { + @apply text-center mt-24; +} + /* Inline code - add background and padding, remove backticks */ .prose :where(code):not(:where([class~='not-prose'], pre *)) { @apply text-gray-900 font-medium text-[0.9em] bg-gray-100 px-1.5 py-0.5 rounded; From 7c22d348a0befacd277584e5fff2fca7a339c4b4 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 00:53:06 -0800 Subject: [PATCH 22/40] fix header styling --- pages/index.mdx | 6 +++--- src/index.css | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/index.mdx b/pages/index.mdx index 390270a..7b3aa08 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -13,13 +13,15 @@ lang: "en" Scratch is a tool for writing with [Markdown](https://daringfireball.net/projects/markdown/) and [React](https://react.dev/). -Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites like this one. +Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites like [this one](https://github.com/scratch/scratch.dev). Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask a coding agent for help when it's easier to express yourself with code. ## Quick Start +Scratch requires no configuration so it's easy to get started + ```bash # Install scratch curl -fsSL https://scratch.dev/install.sh | bash @@ -37,8 +39,6 @@ Scratch lets you write in Markdown and embed interactive React components, like -Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask a coding agent for help when it's easier to express yourself with code. - You can use React components to style text or embed fully working demos in your product specs: diff --git a/src/index.css b/src/index.css index 3789800..d57654e 100644 --- a/src/index.css +++ b/src/index.css @@ -16,8 +16,8 @@ @apply no-underline hover:underline; } -/* Headings - add top margin and center */ -.prose :where(h1, h2, h3, h4, h5, h6):not(:where([class~='not-prose'] *)) { +/* H1 - center and add extra top margin */ +.prose :where(h1):not(:where([class~='not-prose'] *)) { @apply text-center mt-24; } From 837e6b889e76a61eb7ffbe2afe767f314dcb1f80 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 01:01:26 -0800 Subject: [PATCH 23/40] clean up todo list --- pages/components/TodoList.tsx | 60 +++++++++++++++++------------------ pages/index.mdx | 4 +-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/pages/components/TodoList.tsx b/pages/components/TodoList.tsx index a1085ab..878f38d 100644 --- a/pages/components/TodoList.tsx +++ b/pages/components/TodoList.tsx @@ -8,6 +8,13 @@ interface Todo { const STORAGE_KEY = "scratch-demo-todos"; +const DEFAULT_TODOS: Todo[] = [ + { id: 1, text: "Create scratch project", completed: false }, + { id: 2, text: "Edit pages/index.mdx", completed: false }, + { id: 3, text: "Build with `scratch build`", completed: false }, + { id: 4, text: "Publish!", completed: false }, +]; + let globalTodos: Todo[] | null = null; let listeners: Set<(todos: Todo[]) => void> = new Set(); @@ -17,7 +24,7 @@ function getTodos(): Todo[] { globalTodos = []; } else { const stored = localStorage.getItem(STORAGE_KEY); - globalTodos = stored ? JSON.parse(stored) : []; + globalTodos = stored ? JSON.parse(stored) : DEFAULT_TODOS; } } return globalTodos; @@ -89,30 +96,9 @@ export default function TodoList() { return (
-
- setInput(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && handleAdd()} - placeholder="Add a todo..." - className="flex-1 px-3 py-2 border border-gray-300 rounded-md bg-white text-gray-900 placeholder-gray-400" - /> - -
- - {todos.length === 0 ? ( -

- No todos yet. Add one above! -

- ) : ( -
    - {todos.map((todo) => ( +

    Todo List Demo

    +
      + {todos.map((todo) => (
    • ))} -
    - )} - -
    +
  • + + setInput(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleAdd()} + placeholder="Add a todo..." + className="flex-1 bg-transparent border-b border-gray-300 text-gray-900 placeholder-gray-400 focus:outline-none focus:border-gray-500" + /> +
  • +
+ +
{todos.filter((t) => !t.completed).length} remaining diff --git a/pages/index.mdx b/pages/index.mdx index 7b3aa08..edad553 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -13,7 +13,7 @@ lang: "en" Scratch is a tool for writing with [Markdown](https://daringfireball.net/projects/markdown/) and [React](https://react.dev/). -Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites like [this one](https://github.com/scratch/scratch.dev). +Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites (like [this one](https://github.com/scratch/scratch.dev)) that can be hosted anywhere. Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask a coding agent for help when it's easier to express yourself with code. @@ -35,7 +35,7 @@ scratch dev ## What can you do with Scratch? -Scratch lets you write in Markdown and embed interactive React components, like this counter: +Scratch embed interactive React components into your writing, like this counter: From 08f0d8fbb8536c74a6e099fcf622abf8da073ebb Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 01:02:39 -0800 Subject: [PATCH 24/40] more todo cleanup --- pages/components/TodoList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/components/TodoList.tsx b/pages/components/TodoList.tsx index 878f38d..3a031df 100644 --- a/pages/components/TodoList.tsx +++ b/pages/components/TodoList.tsx @@ -150,7 +150,7 @@ export default function TodoList() { onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAdd()} placeholder="Add a todo..." - className="flex-1 bg-transparent border-b border-gray-300 text-gray-900 placeholder-gray-400 focus:outline-none focus:border-gray-500" + className="flex-1 bg-transparent border-b border-transparent text-gray-900 placeholder-gray-400 focus:outline-none focus:border-gray-300" /> From 123d2cd29f84e56c710952812e0090551335f05c Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 01:54:07 -0800 Subject: [PATCH 25/40] more docs progress --- pages/components/TodoList.tsx | 2 +- pages/docs.mdx | 364 +++++++++++++++++++++++++++++++++- pages/index.mdx | 83 +------- 3 files changed, 369 insertions(+), 80 deletions(-) diff --git a/pages/components/TodoList.tsx b/pages/components/TodoList.tsx index 3a031df..0b3ffdd 100644 --- a/pages/components/TodoList.tsx +++ b/pages/components/TodoList.tsx @@ -79,7 +79,7 @@ function useTodos() { }; const reset = () => { - updateTodos([]); + updateTodos([...DEFAULT_TODOS]); }; return { todos, addTodo, toggleTodo, deleteTodo, reset }; diff --git a/pages/docs.mdx b/pages/docs.mdx index 5076ad8..85d55d6 100644 --- a/pages/docs.mdx +++ b/pages/docs.mdx @@ -1,6 +1,366 @@ --- -title: Docs -description: Documentation for Scratch +title: Documentation +description: Complete documentation for the Scratch CLI --- # Documentation + +## Quick Start + +Scratch requires no configuration so it's easy to get started: + +```bash +# Install scratch +curl -fsSL https://scratch.dev/install.sh | bash + +# Create a new project +scratch create + +# Start the dev server +scratch dev +``` + +Now you're ready to start writing in `pages/index.mdx`. + +## Project Structure + +Scratch uses an opinionated project structure and requires **no boilerplate or configuration**: just create a project, run the dev server, and start writing. + +A simple Scratch project (created with `scratch create`) looks like this: + + +``` +my-scratch-project/ + pages/ # put markdown and components here + posts/ (collapsed) + post1.md # accessible at /posts/post1 + post2.md # /posts/post2 + blog.png # static assets in pages/ or public/ + index.mdx # accessible at root path (/) + Counter.tsx + public/ (collapsed) # static assets + favicon.svg + src/ (collapsed) # global components and css + PageWrapper.jsx # wraps every page + tailwind.css # global styles + markdown/ (collapsed) # default markdown components + index.ts + CodeBlock.tsx + Heading.tsx + Link.tsx + AGENTS.md # agent context + package.json # dependencies + .gitignore +``` + + +Use `scratch build` to compile this project into a static website, like [this one](https://github.com/scratch/scratch.dev). + +### pages/ + +Markdown files live in `pages/` and can be either MDX (`.mdx`) or vanilla Markdown (`.md`). + +Scratch compiles Markdown file into a static web page whose route is determined by your project's directory structure: +- `pages/index.mdx` will be served at the root path (`/`) +- `pages/posts/post1.md` will be served at `pages/posts/post1` + +Component files and libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your Markdown files as long as the component name (`Counter`) matches the component name (`Counter.jsx` or `Counter.tsx`). + +`pages/` can also contain static content like images. + + +Modify `src/tailwind.css` to change the styling of your document. Add headers, footers and other site-wide elements by modifying `src/PageWrapper.jsx`. + +### URL Path Resolution + +| File | URL | +|------|-----| +| `pages/index.mdx` | `/` | +| `pages/about.mdx` | `/about/` | +| `pages/posts/index.mdx` | `/posts/` | +| `pages/posts/hello.mdx` | `/posts/hello/` | + +## Commands + +### scratch create + +Create a new Scratch project. + +```bash +scratch create [path] +``` + +**Options:** +- `--no-src` - Skip the src/ template directory +- `--no-package` - Skip package.json template +- `--no-example` - Skip example content files + +### scratch dev + +Start the development server with live reload. + +```bash +scratch dev [path] +``` + +**Options:** +- `-p, --port ` - Port for dev server (default: 5173) +- `-n, --no-open` - Don't open browser automatically +- `-d, --development` - Development mode (unminified, with source maps) +- `-b, --base ` - Base path for deployment (e.g., `/mysite/`) +- `--static ` - Static file mode: `public`, `assets`, `all` +- `--strict` - Disable auto-injection of PageWrapper and imports +- `--highlight ` - Syntax highlighting: `off`, `popular`, `auto`, `all` + +### scratch build + +Build the project for production. + +```bash +scratch build [path] +``` + +**Options:** +- `-o, --out-dir ` - Output directory (default: `dist`) +- `-d, --development` - Development mode (unminified, with source maps) +- `-b, --base ` - Base path for deployment +- `--no-ssg` - Disable static site generation +- `--static ` - Static file mode: `public`, `assets`, `all` +- `--strict` - Disable auto-injection of PageWrapper and imports +- `--highlight ` - Syntax highlighting mode + +### scratch preview + +Preview the production build locally. + +```bash +scratch preview [path] +``` + +**Options:** +- `-p, --port ` - Port for preview server (default: 4173) +- `-n, --no-open` - Don't open browser automatically + +### scratch view + +Quick preview of a single file or directory. + +```bash +scratch view +``` + +**Options:** +- `-p, --port ` - Port for dev server (default: 5173) +- `-n, --no-open` - Don't open browser automatically + +Useful for viewing individual `.md` or `.mdx` files without setting up a full project. + +### scratch clean + +Remove build artifacts. + +```bash +scratch clean [path] +``` + +Removes the `dist/` and `.scratch-build-cache/` directories. + +### scratch checkout + +Restore template files from built-in templates. + +```bash +scratch checkout [file] +``` + +**Options:** +- `-l, --list` - List all available template files +- `-f, --force` - Overwrite existing files without confirmation + +### scratch update + +Update Scratch to the latest version. + +```bash +scratch update +``` + +### Global Options + +These options work with all commands: + +- `-v, --verbose` - Verbose output for debugging +- `-q, --quiet` - Quiet mode (errors only) +- `--show-bun-errors` - Show full Bun error stack traces +- `--version` - Show CLI version + +## Writing Content + +### Frontmatter + +Add YAML frontmatter to set page metadata: + +```mdx +--- +title: My Page +description: A description for SEO +image: /og-image.png +keywords: ["react", "markdown"] +author: Your Name +--- +``` + +These are automatically injected as HTML meta tags. + +### Using Components + +Components in `src/` or `pages/` are automatically available in MDX files: + +```mdx +# My Page + + + + +``` + +The component filename must match the component name (e.g., `Counter.tsx` exports `Counter`). + +### Component Patterns + +**Self-closing** (wrapped in `not-prose` div): +```mdx + +``` + +**Inline children:** +```mdx + +``` + +**Block children** (requires blank lines): +```mdx + + +## Warning + +This is **markdown** inside a component. + + +``` + +## Styling + +### Tailwind CSS + +Scratch uses Tailwind CSS. Edit `src/tailwind.css` for global styles: + +```css +@import 'tailwindcss'; +@plugin "@tailwindcss/typography"; + +/* Custom styles */ +.prose a { + @apply text-blue-600 hover:text-blue-800; +} +``` + +### Typography + +Markdown content is styled with [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography). Use prose modifiers in `PageWrapper.jsx`: + +```jsx +
+ {children} +
+``` + +Element modifiers: `prose-h1:`, `prose-a:`, `prose-p:`, `prose-code:`, `prose-pre:`, etc. + +### Excluding from Prose + +Use the `not-prose` class to exclude elements from typography styling: + +```jsx +
+ +
+``` + +## PageWrapper + +Create `src/PageWrapper.jsx` to wrap all pages with a layout: + +```jsx +export default function PageWrapper({ children }) { + return ( +
+ +
{children}
+
...
+
+ ); +} +``` + +## Custom Markdown Components + +Override default markdown rendering by creating components in `src/markdown/`: + +```tsx +// src/markdown/Link.tsx +export default function Link({ href, children, ...props }) { + const isExternal = href?.startsWith('http'); + return ( + + {children} + + ); +} +``` + +Export from `src/markdown/index.ts`: + +```ts +export { default as a } from './Link'; +export { default as pre } from './CodeBlock'; +export { default as h1, default as h2 } from './Heading'; +``` + +## Syntax Highlighting + +Code blocks are highlighted with [Shiki](https://shiki.style/). Control highlighting with the `--highlight` flag: + +- `off` - No syntax highlighting +- `popular` - Common languages only +- `auto` - Detect from code blocks (default) +- `all` - All languages + +## Deployment + +Build your site and deploy the `dist/` folder to any static host: + +```bash +scratch build +``` + +For subdirectory deployment, use the `--base` flag: + +```bash +scratch build --base /my-site/ +``` + +## Generated Files + +Add these to `.gitignore`: + +``` +dist/ +.scratch-build-cache/ +node_modules/ +``` diff --git a/pages/index.mdx b/pages/index.mdx index edad553..9be5ef6 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -20,7 +20,7 @@ Scratch was designed for collaborative writing with coding agents like [Claude C ## Quick Start -Scratch requires no configuration so it's easy to get started +Scratch requires no configuration so it's easy to get started: ```bash # Install scratch @@ -33,17 +33,19 @@ scratch create scratch dev ``` +Now you're ready to start writing in `pages/index.mdx`. + ## What can you do with Scratch? -Scratch embed interactive React components into your writing, like this counter: +Scratch lets you embed interactive React components into your writing, like this counter: -You can use React components to style text or embed fully working demos in your product specs: +You can use components to style text or embed fully working demos in your product specs: -Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography), to render your prose with a clean aesthetic. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). +Scratch uses [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography) to render your prose with a clean aesthetic. It supports [Github-flavored Markdown](https://github.com/remarkjs/remark-gfm) out of the box. Code blocks use syntax highlighting by [Shiki](https://shiki.style/). ```python def greet(name: str) -> str: @@ -52,85 +54,12 @@ def greet(name: str) -> str: print(greet("World")) ``` -Scratch also supports Github-flavored Markdown features like checklists and tables: - -| Feature | Supported? | -|---------|-----------| -| Compiles Markdown, TS, JS & CSS | ✅ | -| Dev server with HMR | ✅ | -| Tailwind CSS styling | ✅ | -| Code syntax highlighting | ✅ | - Unlike traditional word processors, Scratch makes it easy to express any idea. If you can describe it to a coding agent, you can add it to your document: Collaborating with AI makes writing more fun. Scratch makes that easy. -## No Boilerplate - -Scratch uses an opinionated project structure and requires **no boilerplate or configuration**: just create a project, run the dev server with `scratch dev`, and start writing. - -A simple Scratch project (created with `scratch create`) looks like this: - - - - -Use `scratch build` to compile this project into a static website, like [this one](https://github.com/scratch/scratch.dev). - -Component files and libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your .mdx files as long as the filename matches the component name. - -Modify `src/tailwind.css` directory to change the styling of your document. Add headers, footers and other site-wide elements by modifying `src/PageWrapper.jsx`. - -## Commands - -```bash -# Create a new project -scratch create [path] # create project - -# Start dev server with hot module reloading -scratch dev - -# Build for production -scratch build - -# Preview production build locally -scratch preview - -# Serve target file/directory on dev server -scratch view path - -# Remove build artifacts -scratch clean - -# Revert a file to its template version -scratch checkout [file] # revert a file to its template version -scratch checkout --force [file] # overwrite without confirmation -scratch checkout --list # list available template files - -# Update scratch to latest version -scratch update -``` - ## Acknowledgements From a42ab574ea5909de656314d15e34eed24c3ba6ef Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 02:19:00 -0800 Subject: [PATCH 26/40] finish docs --- pages/about.mdx | 6 ----- pages/docs.mdx | 65 +++++++++++++++++++++++++++++++------------------ pages/index.mdx | 1 + src/Header.jsx | 1 - src/index.css | 2 +- 5 files changed, 43 insertions(+), 32 deletions(-) delete mode 100644 pages/about.mdx diff --git a/pages/about.mdx b/pages/about.mdx deleted file mode 100644 index 1e3319e..0000000 --- a/pages/about.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: About -description: About Scratch ---- - -# About diff --git a/pages/docs.mdx b/pages/docs.mdx index 85d55d6..7787141 100644 --- a/pages/docs.mdx +++ b/pages/docs.mdx @@ -1,6 +1,11 @@ --- -title: Documentation -description: Complete documentation for the Scratch CLI +title: "Scratch - Documentation" +image: "/scratch-logo.svg" +description: "Complete documentation for the Scratch CLI" +keywords: ["MDX", "static site", "React", "Bun", "markdown"] +author: "Scratch" +type: "website" +lang: "en" --- # Documentation @@ -26,7 +31,7 @@ Now you're ready to start writing in `pages/index.mdx`. Scratch uses an opinionated project structure and requires **no boilerplate or configuration**: just create a project, run the dev server, and start writing. -A simple Scratch project (created with `scratch create`) looks like this: +A simple Scratch project (created with `scratch create my-scratch-project`) looks like this: ``` @@ -41,13 +46,13 @@ my-scratch-project/ public/ (collapsed) # static assets favicon.svg src/ (collapsed) # global components and css - PageWrapper.jsx # wraps every page - tailwind.css # global styles markdown/ (collapsed) # default markdown components index.ts CodeBlock.tsx Heading.tsx Link.tsx + PageWrapper.jsx # wraps every page + tailwind.css # global styles AGENTS.md # agent context package.json # dependencies .gitignore @@ -61,24 +66,33 @@ Use `scratch build` to compile this project into a static website, like [this on Markdown files live in `pages/` and can be either MDX (`.mdx`) or vanilla Markdown (`.md`). Scratch compiles Markdown file into a static web page whose route is determined by your project's directory structure: -- `pages/index.mdx` will be served at the root path (`/`) -- `pages/posts/post1.md` will be served at `pages/posts/post1` +| File | URL | +|------|-----| +| `pages/index.mdx` | `/` | +| `pages/about.mdx` | `/about/` | +| `pages/posts/index.mdx` | `/posts/` | +| `pages/posts/hello.mdx` | `/posts/hello/` | Component files and libraries can live anywhere in `pages/` and `src/`. They are auto-detected by Scratch and don't need to be explicitly imported in your Markdown files as long as the component name (`Counter`) matches the component name (`Counter.jsx` or `Counter.tsx`). -`pages/` can also contain static content like images. +`pages/` can also contain static content like images. These are copied directly into your output directory (`dist/`) and will be served the same way your compiled Markdown is. +### public/ -Modify `src/tailwind.css` to change the styling of your document. Add headers, footers and other site-wide elements by modifying `src/PageWrapper.jsx`. +Add static content like a favicon or `_redirects` file to the `public/` directory. These are copied directly into your output directory (`dist/`) just like static content in `pages/`. -### URL Path Resolution +### src/ -| File | URL | -|------|-----| -| `pages/index.mdx` | `/` | -| `pages/about.mdx` | `/about/` | -| `pages/posts/index.mdx` | `/posts/` | -| `pages/posts/hello.mdx` | `/posts/hello/` | +`src/` is for CSS files, components and JS/TS libraries. New Scratch projects will contain the following: + +- `src/PageWrapper.jsx` - your Markdown contents will be "wrapped" with this component. Modify it to change page headers, footers, nav bars and other global components. +- `src/tailwind.css` - global styles. Edit this to change the look and feel of your compiled Markdown. +- `src/markdown` - a directory containing default Markdown components. For example, edit `src/markdown/CodeBlock.tsx` to change how compiled code blocks + + +### package.json + +Scratch automatically installs build dependences. You can add additional dependencies by editing `package.json`. ## Commands @@ -90,7 +104,7 @@ Create a new Scratch project. scratch create [path] ``` -**Options:** +Options: - `--no-src` - Skip the src/ template directory - `--no-package` - Skip package.json template - `--no-example` - Skip example content files @@ -103,14 +117,17 @@ Start the development server with live reload. scratch dev [path] ``` -**Options:** +Options: - `-p, --port ` - Port for dev server (default: 5173) - `-n, --no-open` - Don't open browser automatically - `-d, --development` - Development mode (unminified, with source maps) - `-b, --base ` - Base path for deployment (e.g., `/mysite/`) -- `--static ` - Static file mode: `public`, `assets`, `all` +- `--static ` - Static file mode: + - `public` - ignore static assets in `pages/` + - `assets` (default) - serve assets like images, but not javascript or typescript code files like `lib.js` + - `all` - serve assets _and_ code files statically - `--strict` - Disable auto-injection of PageWrapper and imports -- `--highlight ` - Syntax highlighting: `off`, `popular`, `auto`, `all` +- `--highlight ` - Syntax highlighting supported languages: `off`, `popular`, `auto` (default), `all` ### scratch build @@ -120,7 +137,7 @@ Build the project for production. scratch build [path] ``` -**Options:** +Options: - `-o, --out-dir ` - Output directory (default: `dist`) - `-d, --development` - Development mode (unminified, with source maps) - `-b, --base ` - Base path for deployment @@ -143,13 +160,13 @@ scratch preview [path] ### scratch view -Quick preview of a single file or directory. +Quick preview of a single file or directory. Handy for e.g. reading `README.md` files. ```bash scratch view ``` -**Options:** +Options: - `-p, --port ` - Port for dev server (default: 5173) - `-n, --no-open` - Don't open browser automatically @@ -173,7 +190,7 @@ Restore template files from built-in templates. scratch checkout [file] ``` -**Options:** +Options: - `-l, --list` - List all available template files - `-f, --force` - Overwrite existing files without confirmation diff --git a/pages/index.mdx b/pages/index.mdx index 9be5ef6..f1531e1 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -1,5 +1,6 @@ --- title: "Scratch" +image: "/scratch-logo.svg" description: "Write with Markdown and React" keywords: ["MDX", "static site", "React", "Bun", "markdown"] author: "Scratch" diff --git a/src/Header.jsx b/src/Header.jsx index f081cf3..a2c64d9 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -6,7 +6,6 @@ export default function Header() {
diff --git a/src/index.css b/src/index.css index d57654e..b081768 100644 --- a/src/index.css +++ b/src/index.css @@ -23,7 +23,7 @@ /* Inline code - add background and padding, remove backticks */ .prose :where(code):not(:where([class~='not-prose'], pre *)) { - @apply text-gray-900 font-medium text-[0.9em] bg-gray-100 px-1.5 py-0.5 rounded; + @apply text-gray-700 font-medium text-[0.9em] bg-gray-50 px-1.5 py-0.5 rounded; } .prose :where(code):not(:where([class~='not-prose'], pre *))::before, From 4fd54478c816761ae20e1a25fbd7a9dc7831c1eb Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 02:22:01 -0800 Subject: [PATCH 27/40] readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..38eedd1 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +

+ Scratch +

+ +# scratch.dev + +Website for the [Scratch](https://scratch.dev) project From 0ff7e7865418609391c09ae3b8c4eef438d1b060 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 02:35:51 -0800 Subject: [PATCH 28/40] add docs sidebar --- pages/components/DocsSidebar.tsx | 76 ++++++++++++++++++++++++++++++++ pages/docs.mdx | 61 +++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 pages/components/DocsSidebar.tsx diff --git a/pages/components/DocsSidebar.tsx b/pages/components/DocsSidebar.tsx new file mode 100644 index 0000000..c7a7bed --- /dev/null +++ b/pages/components/DocsSidebar.tsx @@ -0,0 +1,76 @@ +import React, { useState, useEffect } from "react"; + +interface TocItem { + id: string; + text: string; +} + +export default function DocsSidebar() { + const [headings, setHeadings] = useState([]); + const [activeId, setActiveId] = useState(""); + + useEffect(() => { + // Find all h2 elements on the page + const h2Elements = document.querySelectorAll("h2"); + const items: TocItem[] = []; + + h2Elements.forEach((h2) => { + // Generate id from text if not present + if (!h2.id) { + h2.id = h2.textContent + ?.toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/(^-|-$)/g, "") || ""; + } + items.push({ + id: h2.id, + text: h2.textContent || "", + }); + }); + + setHeadings(items); + }, []); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveId(entry.target.id); + } + }); + }, + { rootMargin: "-80px 0px -80% 0px" } + ); + + headings.forEach(({ id }) => { + const el = document.getElementById(id); + if (el) observer.observe(el); + }); + + return () => observer.disconnect(); + }, [headings]); + + if (headings.length === 0) return null; + + return ( +
+ ); +} diff --git a/pages/docs.mdx b/pages/docs.mdx index 7787141..0f9ec2f 100644 --- a/pages/docs.mdx +++ b/pages/docs.mdx @@ -10,6 +10,8 @@ lang: "en" # Documentation + + ## Quick Start Scratch requires no configuration so it's easy to get started: @@ -358,6 +360,65 @@ Code blocks are highlighted with [Shiki](https://shiki.style/). Control highligh - `auto` - Detect from code blocks (default) - `all` - All languages +## Build Pipeline + +When you run `scratch build`, Scratch compiles your MDX files into a **fully static website**. There's no server-side code running in production—everything is pre-rendered HTML, CSS, and JavaScript that can be hosted on any static file server. + +Scratch supports defaults that make it easy to create beautiful & functional websites with your writing. Here's a description of the Scratch build pipeline: + +### Build Steps + +#### 1. Dependency Resolution + +Scratch auto-installs npm dependencies from your `package.json` using Bun. Dependencies are cached in `.scratch-build-cache/` for faster subsequent builds. + +#### 2. Entry Generation + +For each `.mdx` or `.md` file in `pages/`, Scratch generates client and server entry files that import your content and wire up React rendering. + +#### 3. MDX Compilation + +MDX files are compiled through a pipeline of remark and rehype plugins: + +**Remark plugins** (process Markdown AST): +- **remark-gfm** - GitHub Flavored Markdown (tables, strikethrough, task lists) +- **remark-frontmatter** - Extracts YAML frontmatter for page metadata +- **remark-auto-import** - Automatically imports components used in MDX without explicit import statements +- **remark-not-prose** - Wraps self-closing components in `not-prose` divs to prevent Tailwind Typography styles from affecting them + +**Rehype plugins** (process HTML AST): +- **rehype-raw** - Allows raw HTML in Markdown while preserving MDX nodes +- **rehype-image-paths** - Transforms relative image paths to absolute routes, handles base paths for subdirectory deployments +- **rehype-shiki** - Syntax highlighting using Shiki with configurable language detection +- **rehype-footnotes** - Moves footnotes inside PageWrapper for proper styling + +#### 4. Tailwind Compilation + +Scratch compiles your CSS using Tailwind CSS v4. Only the utilities actually used in your content are included in the final bundle. + +#### 5. Server Build + +Scratch builds a server bundle to pre-render each page. Scratch generats static website and bundle is only used during the build process--it doesn't actually run on a server. + +#### 6. Client Build + +The client JavaScript is bundled with Bun's bundler. Output is minified for production with content-hashed filenames for cache busting. + +#### 7. HTML Generation + +Each page is rendered to static HTML with: +- Pre-rendered content from the server build +- Injected CSS and JS bundle references +- Meta tags from frontmatter (title, description, og:image, etc.) + +#### 8. Static Assets + +Files from `public/` are copied to the output directory. + +#### 9. Output + +The final static site is written to `dist/`, ready for deployment to any static host. + ## Deployment Build your site and deploy the `dist/` folder to any static host: From 881b748a5f2e1e55216b6c261280e157145eaa62 Mon Sep 17 00:00:00 2001 From: Peter Koomen Date: Sun, 28 Dec 2025 02:39:56 -0800 Subject: [PATCH 29/40] fix sidebar --- pages/components/DocsSidebar.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pages/components/DocsSidebar.tsx b/pages/components/DocsSidebar.tsx index c7a7bed..5f6cc6d 100644 --- a/pages/components/DocsSidebar.tsx +++ b/pages/components/DocsSidebar.tsx @@ -22,13 +22,25 @@ export default function DocsSidebar() { .replace(/[^a-z0-9]+/g, "-") .replace(/(^-|-$)/g, "") || ""; } + // Get text content, filtering out the # anchor + let text = h2.textContent || ""; + text = text.replace(/^#\s*/, "").trim(); items.push({ id: h2.id, - text: h2.textContent || "", + text, }); }); setHeadings(items); + + // Scroll to hash after IDs are set + if (window.location.hash) { + const id = window.location.hash.slice(1); + const el = document.getElementById(id); + if (el) { + setTimeout(() => el.scrollIntoView(), 0); + } + } }, []); useEffect(() => { @@ -54,7 +66,7 @@ export default function DocsSidebar() { if (headings.length === 0) return null; return ( -