1import{r as c,j as p}from"./index-_Sg_1KT6.js";import{u as $e}from"./useCanvas-DRXw8nFR.js";import{u as De}from"./useMouseInteraction-DqIzV1q4.js";import{E as Ne}from"./ExperimentControls-DmwC3FZh.js";import{E as Pe}from"./ExperimentMetrics-D-l2z47K.js";import{E as Ie}from"./ExperimentNav-CPfQyQAJ.js";const Be=[{id:"draft",label:"draft.room()"},{id:"stitch",label:"stitch.threshold()"},{id:"fold",label:"drag.fold()"}],pe=["nave","vesper","aperture","choir","vestige","hinge","suture","catacomb","lantern","oratory","passage","archive"],W=[188,154,206,332,48,282,128,18,92,224,14,172],T=(r,o,u)=>Math.max(o,Math.min(u,r)),Fe=(r,o,u)=>r+(o-r)*u,X=r=>({x:r.x+r.w/2,y:r.y+r.h/2}),be=(r,o)=>r<o?`${r}:${o}`:`${o}:${r}`,me=(r,o,u,e)=>({x:Math.min(r,u),y:Math.min(o,e),w:Math.abs(u-r),h:Math.abs(e-o)}),U=(r,o,u,e,s,l,d)=>({id:r,label:o,x:u,y:e,w:s,h:l,hue:d,vx:0,vy:0,depth:18,breath:Math.random()*Math.PI*2}),ye=(r,o,u)=>{r.x=T(r.x,36,Math.max(36,o-36-r.w)),r.y=T(r.y,46,Math.max(46,u-36-r.h))},ee=(r,o,u)=>{for(let e=r.length-1;e>=0;e--){const s=r[e];if(o>=s.x&&o<=s.x+s.w&&u>=s.y&&u<=s.y+s.h)return s}return null},we=(r,o,u)=>{const e=r.x+r.w/2,s=r.y+r.h/2,l=o-e,d=u-s,P=Math.abs(l),I=Math.abs(d);return P/r.w>I/r.h?{x:e+Math.sign(l||1)*r.w*.5,y:s+d/Math.max(P,1)*r.w*.5}:{x:e+l/Math.max(I,1)*r.h*.5,y:s+Math.sign(d||1)*r.h*.5}},Xe=(r,o,u,e)=>{const s=(m,Y,_)=>(Y.x-m.x)*(_.y-m.y)-(Y.y-m.y)*(_.x-m.x),l=s(r,o,u),d=s(r,o,e),P=s(u,e,r),I=s(u,e,o);return l*d<0&&P*I<0},Ye=(r,o)=>!(r.x+r.w<o.x||o.x+o.w<r.x||r.y+r.h<o.y||o.y+o.h<r.y),Ae=(r,o)=>{if(r.length===0)return 0;const u=new Set(r.map(d=>d.id)),e=new Map(r.map(d=>[d.id,[]]));o.forEach(d=>{!u.has(d.a)||!u.has(d.b)||(e.get(d.a).push(d.b),e.get(d.b).push(d.a))});let s=0;const l=new Set;return r.forEach(d=>{if(l.has(d.id))return;s++;const P=[d.id];for(;P.length;){const I=P.pop();l.has(I)||(l.add(I),e.get(I).forEach(m=>{l.has(m)||P.push(m)}))}}),Math.max(0,o.length-r.length+s)},Te=(r,o)=>{const u=r*.5,e=o*.56;return{rooms:[U("room-1","nave",u-250,e-120,178,92,W[0]),U("room-2","hinge",u-18,e-170,156,86,W[1]),U("room-3","choir",u+170,e-18,184,98,W[2]),U("room-4","vestige",u-126,e+88,164,82,W[3])],edges:[{a:"room-1",b:"room-2",phase:.2},{a:"room-2",b:"room-3",phase:1.1},{a:"room-2",b:"room-4",phase:2.1}],nextId:5}},Ue=({category:r,experiment:o})=>{const{canvasRef:u,ctx:e,dimensions:s}=$e(),l=De(u),[d,P]=c.useState("draft"),[I,m]=c.useState("â´ draft chambers, stitch thresholds, then drag the plan until geometry learns to lie â´"),[Y,_]=c.useState(!0),[H,ve]=c.useState(.62),[L,ke]=c.useState(.68),[O,te]=c.useState({chambers:0,seams:0,loops:0,paradox:0,tension:0}),y=c.useRef([]),C=c.useRef([]),M=c.useRef(null),k=c.useRef(null),J=c.useRef(null),D=c.useRef(null),Q=c.useRef(!1),q=c.useRef(1),K=c.useRef(0),ne=c.useRef(0),A=c.useRef(0),Me=c.useMemo(()=>[{label:"chambers",value:O.chambers},{label:"thresholds",value:O.seams},{label:"loops",value:O.loops},{label:"paradox",value:O.paradox}],[O]),V=c.useCallback(()=>{if(s.width===0||s.height===0)return;const t=Te(s.width,s.height);y.current=t.rooms,C.current=t.edges,q.current=t.nextId,D.current=null,M.current=null,k.current=null,m("â´ a folded blueprint wakes // drag to add more chambers or rewire the thresholds â´")},[s.height,s.width]);c.useEffect(()=>{s.width===0||y.current.length>0||V()},[s.width,V]);const Re=c.useCallback(()=>{y.current=[],C.current=[],M.current=null,k.current=null,J.current=null,D.current=null,q.current=1,te({chambers:0,seams:0,loops:0,paradox:0,tension:0}),m("â´ the blueprint has been erased back to latent paper // drag in draft mode to start again â´")},[]),Ce=c.useCallback(()=>{const t=y.current;t.length===0||s.width===0||(t.forEach(n=>{n.x=T(n.x+(Math.random()-.5)*180,28,s.width-n.w-28),n.y=T(n.y+(Math.random()-.5)*140,40,s.height-n.h-28),n.vx+=(Math.random()-.5)*3,n.vy+=(Math.random()-.5)*3}),A.current=1.4,m("â´ thresholds have been kicked loose // the plan is re-negotiating itself â´"))},[s.height,s.width]),Se=c.useCallback(()=>{y.current.length!==0&&(A.current=2,m("â´ the seams pull inward // architecture tries to remember a stable story â´"))},[]),je=c.useCallback(()=>{_(t=>{const n=!t;return m(n?"â´ afterimages return // every room grows a second opinion of itself â´":"â´ ghost layers withdrawn // only the present floorplan remains â´"),n})},[]),se=c.useCallback(t=>{const n=y.current,a=n.findIndex(f=>f.id===t);if(a<0)return;const[h]=n.splice(a,1);n.push(h)},[]),re=c.useCallback(t=>{if(s.width===0)return;const n=me(t.startX,t.startY,t.currentX,t.currentY);if(n.w<64||n.h<48){m("â´ chamber aborted // space needs a little more body than that â´");return}const a=`room-${q.current}`,h=q.current-1;q.current+=1;const f=pe[h%pe.length],x=W[h%W.length],w=U(a,f,T(n.x,24,s.width-n.w-24),T(n.y,44,s.height-n.h-24),T(n.w,70,240),T(n.h,52,148),x);y.current.push(w),m(`â´ chamber ${f} drafted // stitch it to another threshold or drag it into contradiction â´`)},[s.width,s.height]),ae=c.useCallback(()=>
1{if(!l.isInBounds||s.width===0)return;const{x:t,y:n}=l.positionRef.current;if(d==="draft"){M.current={startX:t,startY:n,currentX:t,currentY:n};return}if(d==="fold"){const a=ee(y.current,t,n);if(!a)return;se(a.id),k.current={id:a.id,offsetX:t-a.x,offsetY:n-a.y},a.depth+=14,m(`â´ ${a.label} lifted // the seams around it begin to tense â´`)}},[se,s.width,d,l.isInBounds,l.positionRef]),ce=c.useCallback(()=>{if(d==="draft"&&M.current){re(M.current),M.current=null;return}if(d==="fold"&&k.current){const t=y.current.find(n=>n.id===k.current.id);k.current=null,A.current=1.25,t&&m(`â´ ${t.label} released // the impossible corridors are redistributing pressure â´`)}},[re,d]),Z=c.useCallback(()=>{if(d!=="stitch"||!l.isInBounds)return;const{x:t,y:n}=l.positionRef.current,a=ee(y.current,t,n);if(!a){D.current=null,m("â´ threshold intent dissolved // click one chamber, then another â´");return}const h=D.current;if(!h){D.current=a.id,m(`â´ first threshold pinned to ${a.label} // choose its counterpart â´`);return}if(h===a.id){D.current=null,m(`â´ ${a.label} released // choose two different chambers to bend space between them â´`);return}const f=be(h,a.id),x=C.current.findIndex(w=>be(w.a,w.b)===f);x>=0?(C.current.splice(x,1),m("â´ threshold severed // those rooms no longer agree on adjacency â´")):(C.current.push({a:h,b:a.id,phase:Math.random()*Math.PI*2}),m("â´ new threshold sutured // the floorplan now has another impossible sentence â´")),D.current=null,A.current=.9},[d,l.isInBounds,l.positionRef]);c.useEffect(()=>{const t=u.current;if(t)return t.addEventListener("click",Z),()=>t.removeEventListener("click",Z)},[u,Z]);const oe=c.useCallback(()=>{if(!l.isInBounds&&!l.isDown)J.current=null;else{const{x:t,y:n}=l.positionRef.current;J.current=ee(y.current,t,n)?.id??null}if(l.isDown&&!Q.current&&ae(),!l.isDown&&Q.current&&ce(),Q.current=l.isDown,d==="draft"&&l.isDown&&M.current&&(M.current.currentX=l.positionRef.current.x,M.current.currentY=l.positionRef.current.y),d==="fold"&&l.isDown&&k.current){const t=y.current.find(n=>n.id===k.current.id);if(!t)return;t.x=l.positionRef.current.x-k.current.offsetX,t.y=l.positionRef.current.y-k.current.offsetY,ye(t,s.width,s.height)}},[s.height,s.width,ae,ce,d,l.isDown,l.isInBounds,l.positionRef]),ie=c.useCallback(()=>{const t=y.current,n=C.current;let a=0,h=0;for(let f=0;f<t.length;f++)for(let x=f+1;x<t.length;x++)Ye(t[f],t[x])&&a++;for(let f=0;f<n.length;f++){const x=n[f],w=t.find(g=>g.id===x.a),B=t.find(g=>g.id===x.b);if(!w||!B)continue;const i=X(w),b=X(B);for(let g=f+1;g<n.length;g++){const v=n[g];if(x.a===v.a||x.a===v.b||x.b===v.a||x.b===v.b)continue;const S=t.find(R=>R.id===v.a),j=t.find(R=>R.id===v.b);if(!S||!j)continue;const F=X(S),N=X(j);Xe(i,b,F,N)&&h++}}return a+h},[]),le=c.useCallback(t=>{const n=y.current,a=C.current;if(n.length===0)return;const h=new Map(n.map(i=>[i.id,i])),f=Math.min(2,t*60),x=(.014+H*.02)*(1+A.current*.65);let w=0,B=0;A.current>.02?A.current*=.94:A.current=0,C.current=a.filter(i=>h.has(i.a)&&h.has(i.b)),C.current.forEach(i=>{const b=h.get(i.a),g=h.get(i.b),v=X(b),S=X(g),j=S.x-v.x,F=S.y-v.y,N=Math.hypot(j,F)||1,R=150+(b.w+g.w)*.24,E=(N-R)/
1R,$=E*x*f,z=j/N,G=F/N;k.current?.id!==b.id&&(b.vx+=z*$,b.vy+=G*$),k.current?.id!==g.id&&(g.vx-=z*$,g.vy-=G*$),i.phase+=t*(.8+Math.abs(E)*4),w+=Math.abs(E),B++});for(let i=0;i<n.length;i++)for(let b=i+1;b<n.length;b++){const g=n[i],v=n[b],S=X(g),j=X(v),F=j.x-S.x,N=j.y-S.y,R=Math.hypot(F,N)||1,E=(g.w+v.w)*.45;if(R>=E)continue;const $=(E-R)/E*.06*f,z=F/R,G=N/R;k.current?.id!==g.id&&(g.vx-=z*$,g.vy-=G*$),k.current?.id!==v.id&&(v.vx+=z*$,v.vy+=G*$)}n.forEach(i=>{const b=C.current.reduce((S,j)=>j.a===i.id||j.b===i.id?S+1:S,0),g=k.current?.id===i.id,v=12+b*7+L*22+(g?22:0);i.depth=Fe(i.depth,v,.08),i.breath+=t*(.6+b*.04),g?(i.vx*=.74,i.vy*=.74):(i.x+=i.vx*f,i.y+=i.vy*f,i.vx*=.8,i.vy*=.8,ye(i,s.width,s.height))}),ne.current%10===0&&te({chambers:n.length,seams:C.current.length,loops:Ae(n,C.current),paradox:ie(),tension:B?w/B:0})},[ie,s.height,s.width,L,H]),he=c.useCallback(()=>{if(!e)return;const t=e.createLinearGradient(0,0,s.width,s.height);t.addColorStop(0,"rgba(2, 8, 16, 0.98)"),t.addColorStop(.45,"rgba(4, 16, 28, 0.98)"),t.addColorStop(1,"rgba(0, 6, 12, 0.98)"),e.fillStyle=t,e.fillRect(0,0,s.width,s.height),e.save(),e.strokeStyle="rgba(102, 255, 204, 0.06)",e.lineWidth=1;const n=36,a=K.current*18%n;for(let h=-n;h<s.width+n;h+=n)e.beginPath(),e.moveTo(h+a,0),e.lineTo(h-n*.7+a,s.height),e.stroke();for(let h=40;h<s.height;h+=34)e.beginPath(),e.moveTo(0,h),e.lineTo(s.width,h),e.stroke();e.restore()},[e,s.height,s.width]),de=c.useCallback(t=>{if(!e)return;const n=y.current.find($=>$.id===t.a),a=y.current.find($=>$.id===t.b);if(!n||!a)return;const h=X(n),f=X(a),x=we(n,f.x,f.y),w=we(a,h.x,h.y),B=(x.x+w.x)/2,i=(x.y+w.y)/2,b=w.x-x.x,g=w.y-x.y,v=Math.hypot(b,g)||1,S=-g/v,j=b/v,F=Math.sin(t.phase)*24+(v-160)*.08,N=B+S*F,R=i+j*F,E=D.current&&(D.current===t.a||D.current===t.b);e.save(),e.lineCap="round",e.shadowBlur=E?24:16,e.shadowColor=E?"rgba(255, 220, 120, 0.35)":"rgba(102, 255, 204, 0.22)",e.strokeStyle=E?"rgba(255, 224, 146, 0.85)":"rgba(102, 255, 204, 0.42)",e.lineWidth=E?5:3,e.beginPath(),e.moveTo(x.x,x.y),e.quadraticCurveTo(N,R,w.x,w.y),e.stroke(),e.strokeStyle=E?"rgba(255, 247, 204, 0.95)":"rgba(210, 255, 245, 0.35)",e.lineWidth=1.3,e.setLineDash([8,18]),e.lineDashOffset=-K.current*120,e.beginPath(),e.moveTo(x.x,x.y),e.quadraticCurveTo(N,R,w.x,w.y),e.stroke(),e.restore()},[e]),ue=c.useCallback(t=>{if(!e)return;const n=J.current===t.id,a=D.current===t.id,h=Math.cos(t.breath)*L*6,f=Math.sin(t.breath*.9)*L*4,x=t.depth;if(Y)for(let b=3;b>=1;b--){const g=.045*b;e.fillStyle=`hsla(${t.hue}, 80%, ${18+b*5}%, ${g})`,e.strokeStyle=`hsla(${t.hue}, 75%, 62%, ${g*1.6})`,e.lineWidth=1,e.beginPath(),e.roundRect(t.x+h*b*.52,t.y-x*b*.18+f*b*.52,t.w,t.h,12),e.fill(),e.stroke()}e.save();const w=t.x+h,B=t.y-x*.24+f;e.fillStyle=`hsla(${t.hue}, 82%, 15%, 0.82)`,e.strokeStyle=`hsla(${t.hue}, 86%, 56%, ${n||a?.8:.34})`,e.lineWidth=n||a?2.4:1.2,e.beginPath(),e.roundRect(w,B,t.w,t.h,14),e.fill(),e.stroke();const i=e.createLinearGradient(t.x,t.y,t.x+t.w,t.y+t.h);i.addColorStop(0,`hsla(${t.hue}, 76%, 18%, 0.94)`),i.addColorStop(1,`hsla(${t.hue+18}, 82%, 10%, 0.94)`),e.fillStyle=i,e.shadowBlur=n||a?26:16,e.shadowColor=`hsla(${t.hue}, 92%, 62%, ${n||a?.34:.16})`,e.beginPath(),e.roundRect(t.x,t.y,t.w,t.h,14),e.fill(),e.strokeStyle=`hsla(${t.hue}, 92%, ${n||a?74:62}%, ${n||a?.96:.58})`,e.lineWidth=n||a?2.3:1.4,e.stroke(),e.strokeStyle=`hsla(${t.hue}, 90%, 70%, 0.18)`,e.lineWidth=1,e.setLineDash([10,8]),e.lineDashOffset=-K.current*40,e.strokeRect(t.x+12,t.y+18,Math.max(10,t.w-24),Math.max(10,t.h-36)),e.setLineDash([]),e.fillStyle="rgba(225, 255, 244, 0.9)",e.font="12px SF Mono, Monaco, monospace",e.textBaseline="top",e.fillText(t.label,t.x+12,t.y+10),e.fillStyle=`hsla(${t.hue}, 90%, 72%, 0.7)`,e.font="10px SF Mono, Monaco, monospace",e.fillText(`#${t.id.split("-")[1]}`,t.x+12,t.y+t.h-18),a&&(e.strokeStyle="rgba(255, 228, 132, 0.95)",e.lineWidth=2,e.beginPath(),e.roundRect(t.x-6,t.y-6,t.w+12,t.h+12,18),e.stroke()),e.restore()},[e,Y,L]),fe=c.useCallback(()=>{if(!e||!M.current)return;const t=me(M.current.startX,M.current.startY,M.current.currentX,M.current.currentY);e.save(),e.fillStyle="rgba(102, 255, 204, 0.09)",e.strokeStyle="rgba(102, 255, 204, 0.8)",e.lineWidth=1.5,e.setLineDash([10,8]),e.beginPath(),e.roundRect(t.x,t.y,t.w,t.h,12),e.fill(),e.stroke(),e.restore(
1)},[e]),xe=c.useCallback(()=>{!e||s.width===0||(he(),C.current.forEach(de),y.current.forEach(ue),fe(),y.current.length===0&&(e.save(),e.fillStyle="rgba(102, 255, 204, 0.28)",e.font="14px SF Mono, Monaco, monospace",e.textAlign="center",e.fillText("â´ drag to draft the first chamber â´",s.centerX,s.centerY),e.restore()))},[e,s.centerX,s.centerY,s.width,he,fe,de,ue]),ge=c.useCallback(t=>{!e||s.width===0||(K.current+=t,ne.current++,oe(),le(t),xe())},[e,s.width,xe,le,oe]);c.useEffect(()=>{if(!e||s.width===0)return;let t,n=performance.now();const a=h=>{const f=Math.min(.05,(h-n)/1e3)||.016;n=h,ge(f),t=requestAnimationFrame(a)};return t=requestAnimationFrame(a),()=>cancelAnimationFrame(t)},[e,s.width,ge]);const Ee=[{id:"settle",label:"settle()",onClick:Se},{id:"scatter",label:"scatter()",onClick:Ce},{id:"echoes",label:Y?"echo.on()":"echo.off()",onClick:je,active:Y},{id:y.current.length===0?"seed":"clear",label:y.current.length===0?"seed.plan()":"clear()",onClick:y.current.length===0?V:Re,variant:"reset"}];return p.jsxs("div",{className:"fixed inset-0 flex flex-col",children:[p.jsxs("header",{className:"relative z-50 flex items-center justify-between p-2 sm:p-4 border-b border-void-green/20 bg-void-dark/80 backdrop-blur-sm",children:[p.jsxs("div",{className:"flex items-center gap-2 sm:gap-4",children:[p.jsx(Ie,{currentCategory:r.slug,currentExperiment:o.slug}),p.jsx("h1",{className:"text-xl text-glow hidden sm:block",style:{color:o.color},children:o.name})]}),p.jsx(Pe,{metrics:Me})]}),p.jsxs("div",{className:"flex flex-col gap-3 p-3 sm:p-4 border-b border-void-green/10 bg-void-dark/60 backdrop-blur-sm",children:[p.jsxs("div",{className:"flex flex-col lg:flex-row items-start lg:items-center justify-between gap-3",children:[p.jsx(Ne,{modes:Be,currentMode:d,onModeChange:P,controls:Ee}),p.jsx("p",{className:"text-void-green/60 text-xs sm:text-sm font-mono max-w-2xl text-left lg:text-right",children:I})]}),p.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[p.jsxs("div",{className:"flex flex-col gap-1",children:[p.jsxs("label",{className:"text-void-cyan/70 text-xs font-mono",children:["fold pressure: ",p.jsx("span",{className:"text-void-green",children:H.toFixed(2)})]}),p.jsx("input",{type:"range",min:"0.1",max:"1",step:"0.01",value:H,onChange:t=>ve(parseFloat(t.target.value)),className:"w-full h-1 bg-void-green/20 rounded-lg appearance-none cursor-pointer"})]}),p.jsxs("div",{className:"flex flex-col gap-1",children:[p.jsxs("label",{className:"text-void-cyan/70 text-xs font-mono",children:["echo depth: ",p.jsx("span",{className:"text-void-green",children:L.toFixed(2)})]}),p.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:L,onChange:t=>ke(parseFloat(t.target.value)),className:"w-full h-1 bg-void-green/20 rounded-lg appearance-none cursor-pointer"})]})]})]}),p.jsxs("div",{className:"flex-1 min-h-0 relative bg-void-dark",children:[p.jsx("canvas",{ref:u,className:`absolute inset-0 w-full h-full ${d==="fold"?"cursor-grab active:cursor-grabbing":"cursor-crosshair"}`,"data-testid":"threshold-fold-canvas"}),p.jsxs("div",{className:"absolute bottom-3 left-3 right-3 z-10 flex justify-between gap-3 pointer-events-none",children:[p.jsxs("p",{className:"max-w-md text-[11px] sm:text-xs font-mono text-void-green/45",children:["draft: drag out chambers",p.jsx("br",{}),"stitch: click two chambers to add or sever a threshold",p.jsx("br",{}),"fold: drag rooms until the seams generate paradox"]}),p.jsxs("p",{className:"hidden md:block text-[11px] sm:text-xs font-mono text-void-cyan/45 text-right",children:["mean seam strain: ",(O.tension*100).toFixed(1),"%",p.jsx("br",{}),"echoes ",Y?"enabled":"muted"," // loops ",O.loops]})]})]})]})};export{Ue as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.