claude-live 2.0.8 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,85 +1,68 @@
1
1
  # claude-live
2
2
 
3
- Real-time visualization of [Claude Code](https://docs.anthropic.com/en/docs/claude-code) activity as an orbital solar system.
3
+ **[Try the live demo](https://marisancans.github.io/claude-live/)** no install needed
4
4
 
5
5
  ![claude-live demo](claude-live-demo.gif)
6
6
 
7
- **[Try the demo](https://marisancans.github.io/claude-live/)** (no install needed)
7
+ Real-time visualization of Claude Code activity as a 3D orbital system.
8
8
 
9
9
  ## Install
10
10
 
11
- ### Via Claude plugin marketplace (recommended)
11
+ ### Claude Code Marketplace (recommended)
12
12
 
13
- ```bash
14
- claude plugin marketplace add marisancans/claude-live
13
+ Search for **claude-live** in the Claude Code marketplace, or run inside Claude Code:
14
+
15
+ ```
16
+ /plugins install marisancans/claude-live
15
17
  ```
16
18
 
17
- Hooks are configured automatically. Events stream in the background as you work.
19
+ This gives you the `/claude-live:server` slash command to manage the server from within Claude Code.
18
20
 
19
- ### Via npm (manual setup)
21
+ ### npm (standalone)
20
22
 
21
23
  ```bash
22
24
  npm install -g claude-live
23
25
  ```
24
26
 
25
- Then add hooks to your `~/.claude/settings.json`:
27
+ ## Use
26
28
 
27
- ```json
28
- {
29
- "hooks": {
30
- "PreToolUse": [{ "hooks": [{ "type": "command", "command": "node $(npm root -g)/claude-live/bin/hook.js", "async": true }] }],
31
- "PostToolUse": [{ "hooks": [{ "type": "command", "command": "node $(npm root -g)/claude-live/bin/hook.js", "async": true }] }]
32
- }
33
- }
29
+ ```bash
30
+ claude-live # Start server (foreground)
31
+ claude-live start # Start server in background
34
32
  ```
35
33
 
36
- Run `npm root -g` to find your global node_modules path if the above doesn't work on your shell.
34
+ Open http://localhost:43451 in your browser. Sessions appear automatically as you use Claude Code.
37
35
 
38
- ## Use
39
-
40
- Once installed, use the `/claude-live:server` slash command in Claude Code:
36
+ With the plugin installed, manage the server from inside Claude Code:
41
37
 
42
38
  ```
43
39
  /claude-live:server # Check status, auto-start if needed
44
40
  /claude-live:server stop # Stop the server
45
41
  /claude-live:server restart # Restart the server
46
42
  /claude-live:server logs # Show last 30 log lines
47
- /claude-live:server config # Show current endpoint URL
48
- /claude-live:server config http://192.168.1.50:43451 # Set remote endpoint
49
- /claude-live:server config reset # Reset to localhost default
50
43
  ```
51
44
 
52
- Then open http://localhost:43451 in your browser.
53
-
54
45
  ## How It Works
55
46
 
56
- Sessions appear as star systems. Files orbit as planets -- the more a file is touched, the larger it grows. Tool calls animate between nodes:
57
-
58
- - **Read / Grep / Glob** -- scanner at the file, data streams back to core
59
- - **Edit / Write** -- ink beam fires from core to file
60
- - **Bash** -- terminal window appears at the node
61
- - **Subagents** -- satellite systems tethered to their parent
47
+ The server watches Claude Code's session transcript files (`~/.claude/projects/`) and tails them in real time. No hooks or additional configuration required.
62
48
 
63
- Multiple Claude sessions show as separate star systems. Prompts fly inward, responses fly outward. Context compaction triggers an implosion/rebirth effect.
64
-
65
- Under the hood: the plugin sends every event to a lightweight Node.js server (pure passthrough, no persistence). The server broadcasts events to a PixiJS frontend via Server-Sent Events (SSE).
49
+ Each Claude Code session appears as an orbiting star system. Every tool call creates a node — the more events a session generates, the larger it grows. Multiple sessions show as separate star systems. Prompts pulse outward, context compaction triggers an implosion/rebirth effect.
66
50
 
67
51
  ## Server Endpoints
68
52
 
69
53
  | Endpoint | Method | Description |
70
54
  |---|---|---|
71
- | `/hook` | POST | Receive hook events, broadcast to SSE clients |
55
+ | `/hook` | POST | Receive hook events (legacy/plugin), broadcast to SSE clients |
72
56
  | `/events` | GET | SSE stream for the frontend |
73
- | `/health` | GET | Health check — returns `{"ok":true,"clients":<N>,"port":43451}` |
57
+ | `/health` | GET | Health check — returns `{"ok":true,"version":"X.Y.Z","clients":<N>,"port":43451}` |
74
58
 
75
59
  ## Troubleshooting
76
60
 
77
- | Symptom | Cause | Fix |
78
- |---|---|---|
79
- | No activity in browser | Hooks not firing | Run `/reload-plugins` or check `settings.json` hooks |
80
- | Server not reachable | Server not running | `/claude-live:server` auto-starts it |
81
- | `clients: 0` in `/health` | Server up, no browser tab open | Open `http://localhost:43451` |
82
- | Hook logs location | Debug delivery failures | `~/.config/claude-live/logs/YYYY-MM-DD.jsonl` |
61
+ | Symptom | Fix |
62
+ |---|---|
63
+ | No activity in browser | Make sure Claude Code is running, check `~/.claude/projects/` has `.jsonl` files |
64
+ | Server not reachable | Run `claude-live` to start it |
65
+ | `clients: 0` in `/health` | Open http://localhost:43451 in your browser |
83
66
 
84
67
  ## Development
85
68
 
@@ -91,6 +74,7 @@ npm install
91
74
  node server/index.js # Node.js server on :43451
92
75
  cd client && npm run dev # Vite hot-reload on :7979
93
76
  cd client && npx tsc --noEmit # Type check frontend
77
+ npm run test:server # Run server tests
94
78
  ```
95
79
 
96
80
  ## License
Binary file
@@ -0,0 +1,147 @@
1
+ import{C as W,G as p,a as b,W as z,S as M,P as A,O as S,B as k,b as C,M as F,A as u,d as h,E as j,R,U as L,V,g as E,L as _,h as G,Q as I,k as g,D as f,I as P,r as v,l as o}from"./index-zmiMo1Db.js";class D{constructor(e){this.clock=new W,this.elapsed=0,this.agentGroup=new p,this.currentVisual=null,this.currentVariation=null,this.agentTetherLine=null,this.wanderOffsets=new b(Math.random()*100,Math.random()*100,Math.random()*100),this.pulseTimer=0,this.pulseInterval=2,this._onResize=()=>{const d=this.renderer.domElement.parentElement;if(!d)return;const y=Math.max(1,d.clientWidth),l=Math.max(1,d.clientHeight);this.camera.aspect=y/l,this.camera.updateProjectionMatrix(),this.renderer.setSize(y,l),this.composer.setSize(y,l)},this.renderer=new z({antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.setSize(e.clientWidth,e.clientHeight),this.renderer.setClearColor(197384),e.appendChild(this.renderer.domElement),this.scene=new M;const t=Math.max(1,e.clientWidth),r=Math.max(1,e.clientHeight);this.camera=new A(55,t/r,1,2e3),this.camera.position.set(0,30,110),this.controls=new S(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.06,this.controls.autoRotate=!0,this.controls.autoRotateSpeed=.5,this.controls.target.set(0,15,0),this.controls.minDistance=20,this.controls.maxDistance=300,this.background=new k,this.scene.add(this.background.group);const n=new C(3,16,16),a=new F({color:4491519,transparent:!0,opacity:.5,blending:u}),i=new h(n,a);this.scene.add(i),this.scene.add(this.agentGroup),this.composer=new j(this.renderer),this.composer.addPass(new R(this.scene,this.camera));const c=new L(new V(e.clientWidth,e.clientHeight),1.2,.4,.15);this.composer.addPass(c),this.scene.updateMatrixWorld(!0),this.resizeObserver=new ResizeObserver(this._onResize),this.resizeObserver.observe(e),window.addEventListener("resize",this._onResize),this._onResize()}setVariation(e){if(this.currentVariation=e,this._clearVisual(),this.currentVisual=e.build(this.agentGroup),!this.agentTetherLine){const t=new E().setFromPoints(new Array(21).fill(new b)),r=new _({color:4500223,transparent:!0,opacity:.2,blending:u});this.agentTetherLine=new G(t,r),this.scene.add(this.agentTetherLine)}this.pulseTimer=0}triggerPulse(){var e,t;(t=(e=this.currentVisual)==null?void 0:e.pulse)==null||t.call(e)}_clearVisual(){this.currentVisual&&(this.currentVisual.dispose(),this.currentVisual=null)}tick(){const e=Math.min(this.clock.getDelta(),.05);if(this.elapsed+=e,this.controls.update(),this.background.tick(e),this.currentVisual){this.currentVisual.tick(e,this.elapsed);const t=.25;this.wanderOffsets.x+=e*t*.8,this.wanderOffsets.y+=e*t*1.1,this.wanderOffsets.z+=e*t*.9;const r=50;if(this.agentGroup.position.set(Math.sin(this.wanderOffsets.x)*r,25+Math.sin(this.wanderOffsets.y)*15,Math.cos(this.wanderOffsets.z)*r*Math.cos(this.wanderOffsets.x*.5)),this.agentTetherLine){const n=new b(0,0,0),a=this.agentGroup.position.clone(),i=n.clone().lerp(a,.5);i.y-=10+Math.sin(this.elapsed*2)*4;const c=new I(n,i,a);this.agentTetherLine.geometry.setFromPoints(c.getPoints(20))}this.pulseTimer+=e,this.pulseTimer>=this.pulseInterval&&(this.pulseTimer=0,this.pulseInterval=1.5+Math.random()*3)}try{this.composer.render()}catch{}}destroy(){this.resizeObserver.disconnect(),window.removeEventListener("resize",this._onResize),this.renderer.dispose(),this.renderer.domElement.remove(),this._clearVisual(),this.agentTetherLine&&(this.scene.remove(this.agentTetherLine),this.agentTetherLine.geometry.dispose(),this.agentTetherLine.material.dispose())}}const w=new P(3.5,128),T=new P(3.5,32).toNonIndexed();function O(s){const e=new p;s.add(e);const t=new g({uniforms:{uTime:{value:0},uActivity:{value:0}},vertexShader:`
2
+ uniform float uTime; uniform float uActivity;
3
+ varying vec3 vWorldPos;
4
+
5
+ float getWarp(vec3 p) {
6
+ // Classic smooth, large rolling waves
7
+ return sin(p.x*2.0 - uTime) * sin(p.y*3.0 - uTime*0.8) * cos(p.z*2.0 + uTime) * 1.5;
8
+ }
9
+ void main() {
10
+ vec3 pos = position + normal * getWarp(position) * (0.6 + uActivity);
11
+ vec4 wPos = modelMatrix * vec4(pos, 1.0);
12
+ vWorldPos = wPos.xyz;
13
+ gl_Position = projectionMatrix * viewMatrix * wPos;
14
+ }
15
+ `,fragmentShader:`
16
+ uniform float uActivity;
17
+ varying vec3 vWorldPos;
18
+ void main() {
19
+ vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
20
+ vec3 v = normalize(cameraPosition - vWorldPos);
21
+ float fresnel = 1.0 - max(dot(v, n), 0.0);
22
+
23
+ vec3 base = mix(vec3(0.01, 0.02, 0.05), vec3(0.05, 0.01, 0.05), fract(vWorldPos.y * 0.15));
24
+ vec3 col = base + pow(fresnel, 4.0) * vec3(0.1, 0.4, 0.8) * (1.0 + uActivity);
25
+
26
+ gl_FragColor = vec4(col, min(1.0, 0.3 + pow(fresnel, 4.0) * 0.5));
27
+ }
28
+ `,transparent:!0,depthWrite:!1,blending:u,side:f});e.add(new h(w,t));let r=0;return{tick(n,a){r=Math.max(0,r-n*2),t.uniforms.uTime.value=a,t.uniforms.uActivity.value=r},pulse(){r=1},dispose(){s.remove(e),t.dispose()}}}function B(s){const e=new p;s.add(e);const t=new g({uniforms:{uTime:{value:0},uActivity:{value:0}},vertexShader:`
29
+ uniform float uTime; uniform float uActivity;
30
+ varying vec3 vWorldPos;
31
+
32
+ float getWarp(vec3 p) {
33
+ // Absolute sine waves create sharp folds and spikes instead of smooth curves
34
+ float n1 = 1.0 - abs(sin(p.x * 3.0 + uTime));
35
+ float n2 = 1.0 - abs(cos(p.y * 4.0 - uTime * 1.5));
36
+ float n3 = 1.0 - abs(sin(p.z * 5.0 + uTime * 0.5));
37
+ return pow(n1 * n2 * n3, 2.0) * 3.0; // Pushed out aggressively
38
+ }
39
+ void main() {
40
+ vec3 pos = position + normal * getWarp(position) * (0.8 + uActivity);
41
+ vec4 wPos = modelMatrix * vec4(pos, 1.0);
42
+ vWorldPos = wPos.xyz;
43
+ gl_Position = projectionMatrix * viewMatrix * wPos;
44
+ }
45
+ `,fragmentShader:`
46
+ uniform float uActivity;
47
+ varying vec3 vWorldPos;
48
+ void main() {
49
+ // Flat shading calculation (inherent to dFdx/dFdy on non-indexed geometry)
50
+ vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
51
+ vec3 v = normalize(cameraPosition - vWorldPos);
52
+ float fresnel = 1.0 - max(dot(v, n), 0.0);
53
+
54
+ vec3 col = vec3(0.01, 0.05, 0.05); // Deep green/cyan base
55
+ col += pow(fresnel, 2.0) * vec3(0.1, 0.8, 0.4) * (1.0 + uActivity); // Harsher glow on flat edges
56
+
57
+ gl_FragColor = vec4(col, min(1.0, 0.4 + pow(fresnel, 2.0) * 0.6));
58
+ }
59
+ `,transparent:!0,depthWrite:!1,blending:u,side:f});e.add(new h(T,t));let r=0;return{tick(n,a){r=Math.max(0,r-n*2),t.uniforms.uTime.value=a,t.uniforms.uActivity.value=r},pulse(){r=1},dispose(){s.remove(e),t.dispose()}}}function $(s){const e=new p;s.add(e);const t=new g({uniforms:{uTime:{value:0},uActivity:{value:0}},vertexShader:`
60
+ uniform float uTime; uniform float uActivity;
61
+ varying vec3 vWorldPos;
62
+
63
+ float getWarp(vec3 p) {
64
+ // Ripple strictly outward from the center poles based on Y
65
+ float radius = length(p.xz);
66
+ // Add a vertical sweeping element so it looks like a drop hitting water
67
+ return sin(radius * 15.0 - uTime * 6.0) * cos(p.y * 3.0 + uTime) * 0.5;
68
+ }
69
+ void main() {
70
+ // Displace the sphere entirely radially
71
+ vec3 pos = position + normal * getWarp(position) * (1.0 + uActivity * 2.0);
72
+ vec4 wPos = modelMatrix * vec4(pos, 1.0);
73
+ vWorldPos = wPos.xyz;
74
+ gl_Position = projectionMatrix * viewMatrix * wPos;
75
+ }
76
+ `,fragmentShader:`
77
+ uniform float uActivity;
78
+ varying vec3 vWorldPos;
79
+ void main() {
80
+ vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
81
+ vec3 v = normalize(cameraPosition - vWorldPos);
82
+ float fresnel = 1.0 - max(dot(v, n), 0.0);
83
+
84
+ vec3 col = vec3(0.01, 0.01, 0.08); // Indigo
85
+ col += pow(fresnel, 5.0) * vec3(0.5, 0.1, 1.0) * (1.0 + uActivity);
86
+
87
+ gl_FragColor = vec4(col, min(1.0, 0.2 + pow(fresnel, 5.0) * 0.8));
88
+ }
89
+ `,transparent:!0,depthWrite:!1,blending:u,side:f});e.add(new h(w,t));let r=0;return{tick(n,a){r=Math.max(0,r-n*2),t.uniforms.uTime.value=a,t.uniforms.uActivity.value=r},pulse(){r=1},dispose(){s.remove(e),t.dispose()}}}function H(s){const e=new p;s.add(e);const t=new g({uniforms:{uTime:{value:0},uActivity:{value:0}},vertexShader:`
90
+ uniform float uTime; uniform float uActivity;
91
+ varying vec3 vWorldPos;
92
+
93
+ float getWarp(vec3 p) {
94
+ // Floor the position to calculate the wave, tearing the geometry into blocks
95
+ vec3 stepP = floor(p * 3.0) / 3.0; // Quantize the space
96
+ float n = sin(stepP.x*5.0 - uTime) * cos(stepP.y*5.0) * sin(stepP.z*5.0 + uTime);
97
+ return n * 1.5;
98
+ }
99
+ void main() {
100
+ vec3 pos = position + normal * getWarp(position) * (0.8 + uActivity * 2.0);
101
+ vec4 wPos = modelMatrix * vec4(pos, 1.0);
102
+ vWorldPos = wPos.xyz;
103
+ gl_Position = projectionMatrix * viewMatrix * wPos;
104
+ }
105
+ `,fragmentShader:`
106
+ uniform float uActivity;
107
+ varying vec3 vWorldPos;
108
+ void main() {
109
+ vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
110
+ vec3 v = normalize(cameraPosition - vWorldPos);
111
+ float fresnel = 1.0 - max(dot(v, n), 0.0);
112
+
113
+ vec3 col = vec3(0.05, 0.01, 0.01); // Deep crimson base
114
+ col += pow(fresnel, 2.0) * vec3(1.0, 0.2, 0.2) * (1.0 + uActivity);
115
+
116
+ gl_FragColor = vec4(col, min(1.0, 0.5 + pow(fresnel, 2.0) * 0.5));
117
+ }
118
+ `,transparent:!0,depthWrite:!1,blending:u,side:f}),r=new h(T,t);e.add(r);let n=0;return{tick(a,i){n=Math.max(0,n-a*2),t.uniforms.uTime.value=i,t.uniforms.uActivity.value=n},pulse(){n=1},dispose(){s.remove(e),t.dispose()}}}function q(s){const e=new p;s.add(e);const t=new g({uniforms:{uTime:{value:0},uActivity:{value:0}},vertexShader:`
119
+ uniform float uTime; uniform float uActivity;
120
+ varying vec3 vWorldPos;
121
+
122
+ float getWarp(vec3 p) {
123
+ // Obscenely high frequency sine waves create mathematical noise, vibrating the mesh
124
+ float n = sin(p.x * 40.0 + uTime * 10.0) * sin(p.y * 40.0 - uTime * 5.0) * sin(p.z * 40.0 + uTime * 8.0);
125
+ return n * 0.25; // Keep amplitude low so it looks like fuzz, not explosions
126
+ }
127
+ void main() {
128
+ vec3 pos = position + normal * getWarp(position) * (1.0 + uActivity * 3.0);
129
+ vec4 wPos = modelMatrix * vec4(pos, 1.0);
130
+ vWorldPos = wPos.xyz;
131
+ gl_Position = projectionMatrix * viewMatrix * wPos;
132
+ }
133
+ `,fragmentShader:`
134
+ uniform float uActivity;
135
+ varying vec3 vWorldPos;
136
+ void main() {
137
+ vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
138
+ vec3 v = normalize(cameraPosition - vWorldPos);
139
+ float fresnel = 1.0 - max(dot(v, n), 0.0);
140
+
141
+ vec3 col = vec3(0.01, 0.01, 0.01); // Basically black hole
142
+ // The high frequency normals make the fresnel scatter like crazy!
143
+ col += pow(fresnel, 3.0) * vec3(0.8, 0.8, 1.0) * (1.0 + uActivity);
144
+
145
+ gl_FragColor = vec4(col, min(1.0, 0.6 + pow(fresnel, 3.0) * 0.4));
146
+ }
147
+ `,transparent:!0,depthWrite:!1,blending:u,side:f});e.add(new h(w,t));let r=0;return{tick(n,a){r=Math.max(0,r-n*2),t.uniforms.uTime.value=a,t.uniforms.uActivity.value=r},pulse(){r=1},dispose(){s.remove(e),t.dispose()}}}const x=[{name:"Smooth Void",description:"Deep dark rolling liquid displacement (The tuned baseline model).",build:O},{name:"Jagged Crystal",description:"Changes displacement to absolute mathematics and uses flat-shading to create sharp, tearing crystal spikes.",build:B},{name:"Concentric Droplet",description:"Symmetrical ripples flow outward radially from the center like a drop of liquid suspended in time.",build:$},{name:"Voxel Glitch Glass",description:"Forces the displacement math to snap to blocks (quantization), tearing the glass into digital cubes.",build:H},{name:"Micro-Fuzz",description:"Uses an obscenely high frequency wave to vibrate the vertices microscopically, causing intense light scattering.",build:q}];function U(){const s=v.useRef(null),e=v.useRef(null),t=v.useRef(0),[r,n]=v.useState(0);v.useEffect(()=>{if(!s.current)return;const i=new D(s.current);e.current=i,i.setVariation(x[0]);function c(){i.tick(),t.current=requestAnimationFrame(c)}return t.current=requestAnimationFrame(c),()=>{cancelAnimationFrame(t.current),i.destroy(),e.current=null}},[]);function a(i){var c;n(i),(c=e.current)==null||c.setVariation(x[i])}return o.jsxs("div",{style:{width:"100vw",height:"100vh",background:"#030308",position:"relative",overflow:"hidden"},children:[o.jsx("div",{ref:s,style:{position:"absolute",top:0,left:0,right:0,bottom:0}}),o.jsxs("div",{style:{position:"absolute",top:0,right:0,width:320,height:"100%",background:"rgba(3,3,14,0.92)",borderLeft:"1px solid rgba(255,255,255,0.06)",display:"flex",flexDirection:"column",fontFamily:"'Inter', 'SF Pro Display', system-ui, sans-serif",color:"#aaa",overflowY:"auto"},children:[o.jsxs("div",{style:{padding:"20px 20px 16px",borderBottom:"1px solid rgba(255,255,255,0.06)",background:"linear-gradient(180deg, rgba(100,120,255,0.06) 0%, transparent 100%)"},children:[o.jsx("div",{style:{fontSize:10,letterSpacing:"0.18em",color:"#555",marginBottom:6,textTransform:"uppercase"},children:"SUBAGENT VISUALIZATIONS"}),o.jsx("div",{style:{fontSize:16,color:"#e8ecf4",fontWeight:600,letterSpacing:"-0.01em"},children:"Agent Node Concepts"}),o.jsx("div",{style:{fontSize:11,color:"#556",marginTop:4,lineHeight:1.5},children:"Each concept is a completely different visual direction for representing a subagent in the Three.js scene."})]}),o.jsx("div",{style:{padding:12,display:"flex",flexDirection:"column",gap:8,flex:1},children:x.map((i,c)=>{const d=r===c,l=["#66aaff","#aa66ff","#8833cc","#4488ff","#44ddff"][c];return o.jsxs("button",{onClick:()=>a(c),style:{textAlign:"left",padding:"14px 16px",borderRadius:8,border:`1px solid ${d?l:"rgba(255,255,255,0.06)"}`,background:d?`linear-gradient(135deg, ${l}15 0%, ${l}08 100%)`:"rgba(255,255,255,0.02)",cursor:"pointer",color:"inherit",transition:"all 0.2s ease",position:"relative",overflow:"hidden"},onMouseEnter:m=>{d||(m.currentTarget.style.borderColor=`${l}66`,m.currentTarget.style.background=`${l}08`)},onMouseLeave:m=>{d||(m.currentTarget.style.borderColor="rgba(255,255,255,0.06)",m.currentTarget.style.background="rgba(255,255,255,0.02)")},children:[d&&o.jsx("div",{style:{position:"absolute",left:0,top:0,bottom:0,width:3,background:`linear-gradient(180deg, ${l}, ${l}44)`,borderRadius:"0 2px 2px 0"}}),o.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:6},children:[o.jsx("div",{style:{width:28,height:28,borderRadius:6,background:d?`linear-gradient(135deg, ${l}44, ${l}22)`:"rgba(255,255,255,0.04)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:700,color:d?l:"#555",border:`1px solid ${d?`${l}33`:"rgba(255,255,255,0.06)"}`,flexShrink:0},children:c+1}),o.jsx("span",{style:{fontSize:13,fontWeight:600,letterSpacing:"-0.01em",color:d?"#eef2ff":"#bbb"},children:i.name})]}),o.jsx("div",{style:{fontSize:11,color:d?"#8899bb":"#556",lineHeight:1.6,paddingLeft:38},children:i.description})]},i.name)})}),o.jsx("div",{style:{padding:"12px 16px",borderTop:"1px solid rgba(255,255,255,0.06)"},children:o.jsx("button",{onClick:()=>{var i;return(i=e.current)==null?void 0:i.triggerPulse()},style:{width:"100%",padding:"10px 0",background:"linear-gradient(135deg, rgba(100,120,255,0.15), rgba(100,120,255,0.08))",border:"1px solid rgba(100,120,255,0.2)",borderRadius:6,cursor:"pointer",color:"#8899cc",fontSize:12,fontWeight:600,letterSpacing:"0.05em",transition:"all 0.2s ease"},onMouseEnter:i=>{i.currentTarget.style.background="linear-gradient(135deg, rgba(100,120,255,0.25), rgba(100,120,255,0.15))",i.currentTarget.style.borderColor="rgba(100,120,255,0.4)"},onMouseLeave:i=>{i.currentTarget.style.background="linear-gradient(135deg, rgba(100,120,255,0.15), rgba(100,120,255,0.08))",i.currentTarget.style.borderColor="rgba(100,120,255,0.2)"},children:"⚡ TRIGGER PULSE"})}),o.jsxs("div",{style:{padding:"10px 16px",borderTop:"1px solid rgba(255,255,255,0.04)",fontSize:10,color:"#334",display:"flex",justifyContent:"space-between"},children:[o.jsx("span",{children:"drag to orbit · scroll to zoom"}),o.jsx("span",{children:"auto-pulse active"})]})]}),o.jsx("a",{href:"#/three",style:{position:"absolute",top:16,left:16,fontSize:11,color:"#556",textDecoration:"none",fontFamily:"'Inter', system-ui, sans-serif",letterSpacing:"0.05em",padding:"6px 12px",background:"rgba(255,255,255,0.03)",border:"1px solid rgba(255,255,255,0.06)",borderRadius:5,transition:"all 0.2s ease"},onMouseEnter:i=>{i.currentTarget.style.borderColor="rgba(255,255,255,0.15)",i.currentTarget.style.color="#aab"},onMouseLeave:i=>{i.currentTarget.style.borderColor="rgba(255,255,255,0.06)",i.currentTarget.style.color="#556"},children:"← back"})]})}export{U as AgentDemoScene};
@@ -0,0 +1 @@
1
+ import{C as U,G as O,c as x,W as $,a6 as _,aj as Y,S as X,P as Z,O as J,B as K,a9 as Q,ab as B,d as N,I as W,ak as ee,al as q,M as T,ac as te,E as se,R as ne,U as ie,V as oe,a as p,am as re,A as j,an as ae,ao as ce,Y as G,X as E,v as le,$ as he,a5 as de,_ as pe,r as w,l as h}from"./index-zmiMo1Db.js";class ue{constructor(e){this.clock=new U,this.elapsed=0,this.visualGroup=new O,this.currentVisual=null,this.pulseTimer=0,this.pulseInterval=2.6,this.coreActivity=0,this.accent=new x("#7ec8ff"),this.coreGroup=new O,this.onResize=()=>{const a=this.renderer.domElement.parentElement;if(!a)return;const l=Math.max(1,a.clientWidth),c=Math.max(1,a.clientHeight);this.camera.aspect=l/c,this.camera.updateProjectionMatrix(),this.renderer.setSize(l,c),this.composer.setSize(l,c)},this.renderer=new $({antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.setSize(e.clientWidth,e.clientHeight),this.renderer.setClearColor(263435),this.renderer.outputColorSpace=_,this.renderer.toneMapping=Y,this.renderer.toneMappingExposure=1.08,e.appendChild(this.renderer.domElement),this.scene=new X;const t=Math.max(1,e.clientWidth),s=Math.max(1,e.clientHeight);this.camera=new Z(44,t/s,1,3e3),this.camera.position.set(0,60,164),this.controls=new J(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.06,this.controls.autoRotate=!0,this.controls.autoRotateSpeed=.18,this.controls.target.set(0,18,0),this.controls.minDistance=58,this.controls.maxDistance=320,this.background=new K,this.scene.add(this.background.group),this.scene.add(new Q(13424639,1.2));const n=new B(16777215,.75);n.position.set(90,120,100),this.scene.add(n);const i=new B(8959999,.42);i.position.set(-100,40,-80),this.scene.add(i),this.coreShell=new N(new W(6.4,1),new ee({color:new x("#0b1120"),emissive:this.accent.clone().multiplyScalar(.28),emissiveIntensity:1,specular:new x("#ffffff"),shininess:90,transparent:!0,opacity:.94})),this.coreRing=new N(new q(11.5,.18,12,80),new T({color:this.accent,transparent:!0,opacity:.24})),this.coreRing.rotation.x=Math.PI*.5,this.coreOuterRing=new N(new q(16.5,.12,12,96),new T({color:this.accent.clone().lerp(new x("#ffffff"),.2),transparent:!0,opacity:.12})),this.coreOuterRing.rotation.set(Math.PI*.5,.24,0),this.coreLight=new te(this.accent.getHex(),18,260,2),this.coreGroup.add(this.coreShell,this.coreRing,this.coreOuterRing,this.coreLight),this.coreGroup.position.set(0,12,0),this.scene.add(this.coreGroup),this.scene.add(this.visualGroup),this.composer=new se(this.renderer),this.composer.addPass(new ne(this.scene,this.camera)),this.composer.addPass(new ie(new oe(t,s),.48,.24,.4)),this.resizeObserver=new ResizeObserver(this.onResize),this.resizeObserver.observe(e),window.addEventListener("resize",this.onResize),this.onResize()}setVariation(e){this.clearVisual(),this.applyAccent(e.accent),this.currentVisual=e.build(this.visualGroup,this.camera),this.triggerPulse()}triggerPulse(){var e;this.coreActivity=1,(e=this.currentVisual)==null||e.pulse()}tick(){var t;const e=Math.min(this.clock.getDelta(),.05);this.elapsed+=e,this.pulseTimer+=e,this.coreActivity=Math.max(0,this.coreActivity-e*.62),this.pulseTimer>=this.pulseInterval&&(this.pulseTimer=0,this.pulseInterval=2.3+Math.random()*1.5,this.triggerPulse()),this.controls.update(),this.background.tick(e),(t=this.currentVisual)==null||t.tick(e,this.elapsed),this.tickCore();try{this.composer.render()}catch{}}destroy(){this.resizeObserver.disconnect(),window.removeEventListener("resize",this.onResize),this.clearVisual(),this.background.dispose(),this.controls.dispose(),this.renderer.dispose(),this.renderer.domElement.remove(),this.coreShell.geometry.dispose(),this.coreShell.material.dispose(),this.coreRing.geometry.dispose(),this.coreRing.material.dispose(),this.coreOuterRing.geometry.dispose(),this.coreOuterRing.material.dispose()}applyAccent(e){this.accent.set(e);const t=this.coreShell.material,s=this.coreRing.material,n=this.coreOuterRing.material;t.emissive.copy(this.accent).multiplyScalar(.28),s.color.copy(this.accent),n.color.copy(this.accent).lerp(new x("#ffffff"),.2),this.coreLight.color.copy(this.accent)}tickCore(){const e=1+Math.sin(this.elapsed*.7)*.03,t=1+this.coreActivity*.18,s=this.coreShell.material,n=this.coreRing.material,i=this.coreOuterRing.material;this.coreShell.rotation.y+=.0024,this.coreShell.rotation.x=Math.sin(this.elapsed*.18)*.08,this.coreShell.scale.setScalar(e*t),this.coreRing.rotation.z+=.003+this.coreActivity*.01,this.coreRing.scale.setScalar(1+this.coreActivity*.1),this.coreOuterRing.rotation.y-=.0022,this.coreOuterRing.rotation.z=Math.sin(this.elapsed*.42)*.18,this.coreOuterRing.scale.setScalar(1+this.coreActivity*.16),s.emissiveIntensity=.96+this.coreActivity*.36,n.opacity=.18+this.coreActivity*.14,i.opacity=.08+this.coreActivity*.1,this.coreLight.intensity=14+this.coreActivity*12+Math.sin(this.elapsed*.55)*2}clearVisual(){this.currentVisual&&(this.currentVisual.dispose(),this.currentVisual=null)}}const d=new p(0,12,0),ge=new p(0,1,0),P=new x("#ffffff"),me=new x("#121a2d"),b=new he,f=new p,L=new p,I=new p,M=new x;function fe(o){return o-Math.floor(o)}function g(o){return fe(Math.sin(o*127.1+311.7)*43758.5453123)}function y(o){return g(o)*2-1}function H(o,e){const t=Math.atan2(Math.sin(o-e),Math.cos(o-e));return Math.abs(t)}function D(o,e,t=0,s=Math.PI*2){const n=[],i=o>1?s/(o-1):0,a=t-s*.5;for(let l=0;l<o;l++){const c=s>=Math.PI*2?t+l/o*Math.PI*2:a+i*l;n.push(new p(Math.cos(c),e,Math.sin(c)).normalize())}return n}function A(o,e,t,s=1){const n=o.clone().sub(d);return n.lengthSq()<1e-4?n.set(0,1,0):n.normalize(),{home:o.clone(),position:o.clone(),normal:n,state:"dormant",energy:0,carrierNode:null,meta:e,phase:g(t*3+1)*Math.PI*2,driftAmplitude:s*(.45+g(t*3+2)*.55),driftFrequency:.6+g(t*3+3)*.8}}function S(o,e,t){const s=[];for(let n=0;n<o.length;n++){const i=o[n];i.state==="dormant"&&s.push({index:n,score:t(i)})}return s.sort((n,i)=>n.score-i.score),s.slice(0,e).map(n=>n.index)}function v(o,e){return S(o,e,t=>t.home.distanceToSquared(d))}function ye(){const o=[];for(let e=0;e<84;e++){const t=g(e*13+1)*Math.PI*2,s=16+g(e*13+2)*18,n=.7+g(e*13+3)*.35,i=Math.cos(t)*s*n,a=6+Math.pow(g(e*13+4),.72)*22,l=Math.sin(t)*s*n;o.push(A(new p(d.x+i,d.y+a,d.z+l),{angle:t},e,1))}return o}function be(){const o=[];for(let e=0;e<76;e++){const t=-1+e/75*2+y(e*17+1)*.04,s=14+g(e*17+2)*26,n=t*38,i=6+(1-Math.abs(t)*.7)*14+y(e*17+3)*5,a=y(e*17+4)*(4+s*.12);o.push(A(new p(d.x+n,d.y+i,d.z+a),{sweep:t},e+100,.85))}return o}function xe(){const o=[];for(let e=0;e<82;e++){const t=e%2,s=t===0?-26:26,n=t===0?-7:7,i=s+y(e*19+1)*14,a=7+g(e*19+2)*20,l=n+y(e*19+3)*12;o.push(A(new p(d.x+i,d.y+a,d.z+l),{lobe:t},e+200,.95))}return o}function we(){const o=[];for(let e=0;e<88;e++){const t=e/88*Math.PI*2+y(e*23+1)*.08,s=28+y(e*23+2)*4.8,n=Math.cos(t)*s,i=10+Math.sin(t*2.2)*4+y(e*23+3)*2.4,a=Math.sin(t)*s;o.push(A(new p(d.x+n,d.y+i,d.z+a),{angle:t},e+300,.9))}return o}function Me(){const o=[];for(let t=0;t<80;t++){const s=t%4,n=g(t*29+1)*Math.PI*2,i=8+s*3.8+g(t*29+2)*6,a=Math.cos(n)*i,l=6+s*8.5+y(t*29+3)*2.4,c=Math.sin(n)*i*.82;o.push(A(new p(d.x+a,d.y+l,d.z+c),{angle:n,layer:s},t+400,1.05))}return o}function Se(o,e){const t=g(e*31+1)*Math.PI*2,s=18+g(e*31+2)*10,n=10+g(e*31+3)*18;I.set(d.x+Math.cos(t)*s,d.y+n,d.z+Math.sin(t)*s);const i=S(o,12,a=>a.home.distanceToSquared(I));return i.length?i:v(o,12)}function ve(o,e){const t=-1+e%7/6*2,s=S(o,11,n=>Math.abs((n.meta.sweep??0)-t)*100+Math.abs(n.home.y-(d.y+16)));return s.length?s:v(o,11)}function Pe(o,e){const t=e%2;I.set(d.x+(t===0?-28:28)+y(e*37+1)*6,d.y+10+g(e*37+2)*18,d.z+(t===0?-7:7)+y(e*37+3)*4);const s=S(o,12,n=>((n.meta.lobe??-1)===t?0:1e4)+n.home.distanceToSquared(I));return s.length?s:v(o,12)}function Re(o,e){const t=e*.72,s=S(o,14,n=>H(n.meta.angle??0,t)*180);return s.length?s:v(o,14)}function Ae(o,e){const t=e%4,s=g(e*41+1)*Math.PI*2,n=S(o,10,i=>{const a=(i.meta.layer??-1)===t?0:1e4,l=H(i.meta.angle??0,s)*90;return a+l});return n.length?n:v(o,10)}class ke{constructor(e,t){this.blueprint=t,this.group=new O,this.branchGeometry=new re(1,1,1,6,1,!0),this.branchMaterial=new T({transparent:!0,opacity:.88,depthWrite:!1,blending:j}),this.nodeGeometry=new W(1,1),this.nodeMaterial=new T({transparent:!0,opacity:.96,depthWrite:!1,blending:j}),this.nodes=[],this.segments=[],this.segmentIndexByNode=[],this.pulses=[],this.pulseIndex=0,this.activity=0,this.pendingSteps=0,this.stepAccumulator=0,this.completionDelay=0,this.pulse=()=>{(!this.hasDormantAttractors()&&!this.hasArmedAttractors()||this.segments.length>=this.blueprint.maxSegments-this.blueprint.maxNewPerStep)&&this.reset();const a=this.blueprint.selectTargets(this.attractors,this.pulseIndex).filter(c=>{var r;return((r=this.attractors[c])==null?void 0:r.state)==="dormant"}),l=a.length?a:v(this.attractors,8);for(const c of l){const r=this.attractors[c];r.state==="dormant"&&(r.state="armed",r.energy=1.2)}this.pendingSteps=Math.min(this.pendingSteps+this.blueprint.stepsPerPulse,this.blueprint.stepsPerPulse*4),this.activity=1,this.completionDelay=0,this.pulseIndex+=1,this.spawnPulseBursts(Math.min(3,1+Math.floor(l.length/4)))},this.tick=(a,l)=>{this.activity=Math.max(0,this.activity-a*.46),this.updateAttractors(l,a),this.advanceGrowth(a),this.updatePulses(a,l),this.updateBranchInstances(),this.updateNodeInstances(l),!this.hasDormantAttractors()&&!this.hasArmedAttractors()?(this.completionDelay+=a,this.completionDelay>2.6&&(this.reset(),this.pulse())):this.completionDelay=0},this.dispose=()=>{this.clearPulses(),this.group.removeFromParent(),this.branchGeometry.dispose(),this.branchMaterial.dispose(),this.nodeGeometry.dispose(),this.nodeMaterial.dispose(),this.glowTexture.dispose()},this.accentColor=new x(t.accent),this.branchBaseColor=this.accentColor.clone().lerp(P,.14),this.dormantColor=this.accentColor.clone().lerp(me,.78);const[s,n,i]=ae(t.accent);this.glowTexture=ce(s,n,i,96),this.branchMesh=new G(this.branchGeometry,this.branchMaterial,t.maxSegments),this.branchMesh.instanceMatrix.setUsage(E),this.branchMesh.frustumCulled=!1,this.branchMesh.count=0,this.attractors=t.createAttractors(),this.nodeMesh=new G(this.nodeGeometry,this.nodeMaterial,this.attractors.length),this.nodeMesh.instanceMatrix.setUsage(E),this.nodeMesh.frustumCulled=!1,this.group.add(this.branchMesh,this.nodeMesh),e.add(this.group),this.reset()}reset(){this.clearPulses(),this.nodes=[],this.segments=[],this.segmentIndexByNode=[],this.activity=0,this.pendingSteps=0,this.stepAccumulator=0,this.completionDelay=0,this.pulseIndex=0;for(const e of this.attractors)e.position.copy(e.home),e.state="dormant",e.energy=0,e.carrierNode=null;this.nodes.push({position:d.clone(),parent:null,depth:0,radius:this.blueprint.rootRadius}),this.segmentIndexByNode[0]=-1;for(const e of this.blueprint.seedDirections){f.copy(e).normalize();const t=this.blueprint.stepLength*1.05,s=d.clone().addScaledVector(f,t);this.addNode(0,s,1)}this.updateBranchInstances(),this.updateNodeInstances(0)}updateAttractors(e,t){for(const s of this.attractors){s.energy=Math.max(0,s.energy-t*.72);const n=s.state==="captured"?.18:s.state==="armed"?.42:.1,i=Math.sin(e*s.driftFrequency+s.phase)*s.driftAmplitude*this.blueprint.drift*(n+s.energy*.3);s.position.copy(s.home).addScaledVector(s.normal,i)}}advanceGrowth(e){const s=this.hasArmedAttractors()?3.2:0,n=this.pendingSteps>0?18:s;if(!(n<=0))for(this.stepAccumulator+=e*n;this.stepAccumulator>=1;){this.stepAccumulator-=1;const i=this.growOneStep();if(this.pendingSteps>0&&(this.pendingSteps-=1),!i){this.pendingSteps=0,this.stepAccumulator=0;break}}}growOneStep(){const e=this.blueprint.influenceDistance*this.blueprint.influenceDistance,t=this.blueprint.killDistance*this.blueprint.killDistance,s=new Map,n=new Map;for(const c of this.attractors){if(c.state!=="armed")continue;let r=-1,u=e;for(let z=0;z<this.nodes.length;z++){const V=this.nodes[z].position.distanceToSquared(c.position);V<u&&(u=V,r=z)}if(r<0)continue;const m=this.nodes[r];if(f.copy(c.position).sub(m.position),f.lengthSq()<1e-4)continue;f.normalize();let k=s.get(r);k||(k=new p,s.set(r,k)),k.add(f),n.set(r,(n.get(r)??0)+1)}if(!s.size)return!1;const i=[...s.keys()].sort((c,r)=>(n.get(r)??0)-(n.get(c)??0)),a=[];for(const c of i.slice(0,this.blueprint.maxNewPerStep)){if(this.segments.length>=this.blueprint.maxSegments)break;const r=this.nodes[c],u=s.get(c);if(!u||(f.copy(u).add(this.blueprint.bias),f.lengthSq()<1e-4)||(f.normalize(),L.copy(r.position).addScaledVector(f,this.blueprint.stepLength),!this.isPositionAvailable(L)))continue;const m=this.addNode(c,L,r.depth+1);a.push(m)}if(!a.length)return!1;const l=[];for(const c of this.attractors)if(c.state==="armed"){for(const r of a)if(!(this.nodes[r].position.distanceToSquared(c.position)>t)){c.state="captured",c.energy=1.35,c.carrierNode=r,l.push(r);break}}return l.length&&this.spawnPulseTo(l[l.length-1]),!0}isPositionAvailable(e){const t=this.blueprint.stepLength*.58*(this.blueprint.stepLength*.58);for(const s of this.nodes)if(s.position.distanceToSquared(e)<t)return!1;return!0}addNode(e,t,s){const n=this.nodes[e],i=this.nodes.length,a=Math.max(this.blueprint.tipRadius,n.radius*.9);return this.nodes.push({position:t.clone(),parent:e,depth:s,radius:a}),this.segments.push({startNode:e,endNode:i,radius:Math.max(this.blueprint.tipRadius,a*.92),glow:1.18}),this.segmentIndexByNode[i]=this.segments.length-1,i}updatePulses(e,t){for(const s of this.segments)s.glow=Math.max(0,s.glow-e*.82);for(let s=this.pulses.length-1;s>=0;s--){const n=this.pulses[s];if(n.distance+=e*n.speed,n.distance>=n.totalDistance){this.removePulse(s);continue}let i=0;for(;i<n.distances.length-1&&n.distances[i+1]<n.distance;)i+=1;const a=n.distances[i],l=n.distances[i+1],c=le.clamp((n.distance-a)/Math.max(1e-4,l-a),0,1),r=this.nodes[n.path[i]],u=this.nodes[n.path[i+1]];n.sprite.position.lerpVectors(r.position,u.position,c),n.sprite.scale.setScalar(2.2+Math.sin(t*8+i)*.25+this.activity*.4),n.material.opacity=.8+this.activity*.18;const m=this.segmentIndexByNode[n.path[i+1]];m>=0&&(this.segments[m].glow=Math.max(this.segments[m].glow,1.25))}}updateBranchInstances(){this.branchMesh.count=this.segments.length;for(let e=0;e<this.segments.length;e++){const t=this.segments[e],s=this.nodes[t.startNode].position,n=this.nodes[t.endNode].position;f.copy(n).sub(s);const i=f.length();if(i<1e-4)continue;f.normalize(),b.position.copy(s).lerp(n,.5),b.quaternion.setFromUnitVectors(ge,f);const a=t.radius*(1+t.glow*.3);b.scale.set(a,i,a),b.updateMatrix(),this.branchMesh.setMatrixAt(e,b.matrix),M.copy(this.branchBaseColor).lerp(P,.08+Math.min(t.glow,1.4)*.34),this.branchMesh.setColorAt(e,M)}this.branchMesh.instanceMatrix.needsUpdate=!0,this.branchMesh.instanceColor&&(this.branchMesh.instanceColor.needsUpdate=!0)}updateNodeInstances(e){for(let t=0;t<this.attractors.length;t++){const s=this.attractors[t],n=s.state==="captured"?1.12:s.state==="armed"?.96:.72,i=1+s.energy*.4+Math.sin(e*s.driftFrequency+s.phase)*.04,a=this.blueprint.nodeScale*n*i;b.position.copy(s.position),b.quaternion.identity(),b.scale.setScalar(a),b.updateMatrix(),this.nodeMesh.setMatrixAt(t,b.matrix),s.state==="captured"?M.copy(this.accentColor).lerp(P,.32+s.energy*.14):s.state==="armed"?M.copy(this.accentColor).lerp(P,.16+s.energy*.1):M.copy(this.dormantColor).lerp(this.accentColor,.08),this.nodeMesh.setColorAt(t,M)}this.nodeMesh.instanceMatrix.needsUpdate=!0,this.nodeMesh.instanceColor&&(this.nodeMesh.instanceColor.needsUpdate=!0)}spawnPulseBursts(e){const t=this.attractors.filter(s=>s.state==="captured"&&s.carrierNode!==null);if(t.length)for(let s=0;s<e;s++){const n=t[Math.floor(Math.random()*t.length)];n.carrierNode!==null&&this.spawnPulseTo(n.carrierNode)}}spawnPulseTo(e){if(e<=0||this.pulses.length>=8)return;const t=[];let s=e;for(;s!==null;)t.unshift(s),s=this.nodes[s].parent;if(t.length<2)return;const n=[0];let i=0;for(let c=1;c<t.length;c++){const r=this.nodes[t[c-1]].position,u=this.nodes[t[c]].position;i+=r.distanceTo(u),n.push(i)}const a=new de({map:this.glowTexture,color:P,transparent:!0,opacity:.92,depthWrite:!1,blending:j}),l=new pe(a);l.frustumCulled=!1,l.position.copy(this.nodes[t[0]].position),l.scale.setScalar(2.4),this.group.add(l),this.pulses.push({path:t,distances:n,totalDistance:i,distance:0,speed:24+Math.random()*10,sprite:l,material:a})}removePulse(e){const t=this.pulses[e];t&&(t.sprite.removeFromParent(),t.material.dispose(),this.pulses.splice(e,1))}clearPulses(){for(let e=this.pulses.length-1;e>=0;e--)this.removePulse(e)}hasDormantAttractors(){return this.attractors.some(e=>e.state==="dormant")}hasArmedAttractors(){return this.attractors.some(e=>e.state==="armed")}}function R(o){return{name:o.name,accent:o.accent,layout:o.layout,eventModel:o.eventModel,useCase:o.useCase,description:o.description,build(e){return new ke(e,o)}}}const C=[R({name:"Pocket Canopy",accent:"#7ec8ff",layout:"dome cluster",eventModel:"arm a local neighborhood per event",useCase:"compact session burst around a selected cluster",description:"Closest to the live-node use case: each event wakes a nearby pocket of nodes, and the scaffold grows the smallest readable canopy that still feels organic.",influenceDistance:24,killDistance:4.8,stepLength:3.3,stepsPerPulse:12,maxNewPerStep:5,maxSegments:320,rootRadius:1.18,tipRadius:.22,nodeScale:.92,drift:1.05,bias:new p(0,.18,0),seedDirections:D(5,.9,.15),createAttractors:ye,selectTargets:Se}),R({name:"Ribbon Fan",accent:"#67ddff",layout:"flattened 2.5D fan",eventModel:"events paint narrow sweep bands",useCase:"sidebar card, minimap, or inspector-scale activity view",description:"Same SCA core, but flattened into a shallow fan so you get branching legibility without spending much screen depth.",influenceDistance:22,killDistance:4.4,stepLength:3.2,stepsPerPulse:11,maxNewPerStep:4,maxSegments:280,rootRadius:1.08,tipRadius:.2,nodeScale:.84,drift:.82,bias:new p(0,.1,0),seedDirections:D(5,.55,0,Math.PI*1.05),createAttractors:be,selectTargets:ve}),R({name:"Split Relay",accent:"#a9a0ff",layout:"dual semantic lobes",eventModel:"alternate between left and right node groups",useCase:"tool-vs-agent, request-vs-response, or two-channel routing",description:"Useful when your node field already has two semantic camps. Each event pulls the scaffold into one side, so the branching itself explains the grouping.",influenceDistance:25,killDistance:4.7,stepLength:3.4,stepsPerPulse:12,maxNewPerStep:5,maxSegments:320,rootRadius:1.14,tipRadius:.22,nodeScale:.88,drift:.95,bias:new p(0,.14,0),seedDirections:D(6,.5,Math.PI*.2),createAttractors:xe,selectTargets:Pe}),R({name:"Halo Wreath",accent:"#7cf3e2",layout:"orbital ring",eventModel:"wake a moving arc around a focus target",useCase:"selection halo, focus ring, or surround-state animation",description:"A good fit when the animation should stay compact around one node or cluster. Events wake only one arc, so the ring reads like intent instead of noise.",influenceDistance:27,killDistance:4.5,stepLength:3.25,stepsPerPulse:13,maxNewPerStep:5,maxSegments:340,rootRadius:1.12,tipRadius:.2,nodeScale:.86,drift:.9,bias:new p(0,.04,0),seedDirections:D(7,.22),createAttractors:we,selectTargets:Re}),R({name:"Signal Stack",accent:"#ffb48e",layout:"layered vertical tiers",eventModel:"events climb through stacked bands",useCase:"compact timeline or event ladder with branching memory",description:"This keeps the same attractor logic but turns it into a vertical meter. The result reads more like a time stack than a cloud, while still branching naturally.",influenceDistance:21,killDistance:4.2,stepLength:3.15,stepsPerPulse:10,maxNewPerStep:4,maxSegments:280,rootRadius:1.12,tipRadius:.2,nodeScale:.82,drift:1.08,bias:new p(0,.26,0),seedDirections:[new p(0,1,0).normalize(),new p(.28,.96,.08).normalize(),new p(-.28,.96,-.08).normalize(),new p(.18,.92,-.22).normalize()],createAttractors:Me,selectTargets:Ae})];function F(){return window.innerWidth<920}function Ce(){const o=w.useRef(null),e=w.useRef(null),t=w.useRef(0),[s,n]=w.useState(0),[i,a]=w.useState(()=>F());w.useEffect(()=>{const r=()=>a(F());return window.addEventListener("resize",r),()=>window.removeEventListener("resize",r)},[]),w.useEffect(()=>{if(!o.current)return;const r=new ue(o.current);e.current=r,r.setVariation(C[0]);function u(){r.tick(),t.current=requestAnimationFrame(u)}return t.current=requestAnimationFrame(u),()=>{cancelAnimationFrame(t.current),r.destroy(),e.current=null}},[]);function l(r){var u;n(r),(u=e.current)==null||u.setVariation(C[r])}const c=C[s];return h.jsxs("div",{style:{width:"100vw",height:"100vh",background:"#04050b",position:"relative",overflow:"hidden"},children:[h.jsx("div",{ref:o,style:{position:"absolute",inset:0}}),h.jsxs("div",{style:{position:"absolute",top:i?"auto":0,right:0,bottom:0,width:i?"100%":392,height:i?"48vh":"100%",maxHeight:i?420:"100%",background:"linear-gradient(180deg, rgba(6,8,18,0.97) 0%, rgba(5,7,16,0.9) 100%)",borderLeft:i?"none":"1px solid rgba(255,255,255,0.07)",borderTop:i?"1px solid rgba(255,255,255,0.07)":"none",display:"flex",flexDirection:"column",fontFamily:"'Inter', 'SF Pro Display', system-ui, sans-serif",color:"#c8d1df",overflowY:"auto",backdropFilter:"blur(20px)"},children:[h.jsxs("div",{style:{padding:"20px 20px 16px",borderBottom:"1px solid rgba(255,255,255,0.06)",background:`linear-gradient(180deg, ${c.accent}18 0%, rgba(255,255,255,0) 100%)`},children:[h.jsx("div",{style:{fontSize:10,letterSpacing:"0.18em",color:"#6f7b90",textTransform:"uppercase",marginBottom:8},children:"Space Colonization Lab"}),h.jsx("div",{style:{fontSize:22,fontWeight:680,letterSpacing:"-0.03em",color:"#eef4ff"},children:"5 compact event-animation mappings"}),h.jsx("div",{style:{fontSize:12,lineHeight:1.65,color:"#96a4bc",marginTop:10},children:"Treat nodes as attractors. Each event arms a small pocket of them, the scaffold grows from the core, and pulses travel back through the new branch path."}),h.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:14},children:[h.jsx("span",{style:{padding:"4px 8px",borderRadius:999,fontSize:10,letterSpacing:"0.08em",textTransform:"uppercase",color:c.accent,border:`1px solid ${c.accent}44`,background:`${c.accent}16`},children:c.layout}),h.jsx("span",{style:{fontSize:11,color:"#7f8ca4",alignSelf:"center"},children:c.eventModel})]})]}),h.jsx("div",{style:{padding:"14px 14px 8px",display:"flex",flexDirection:"column",gap:10,flex:1},children:C.map((r,u)=>{const m=u===s;return h.jsxs("button",{onClick:()=>l(u),style:{textAlign:"left",borderRadius:12,border:`1px solid ${m?r.accent:"rgba(255,255,255,0.07)"}`,background:m?`linear-gradient(135deg, ${r.accent}18 0%, rgba(255,255,255,0.03) 100%)`:"rgba(255,255,255,0.025)",padding:"14px 15px",color:"inherit",cursor:"pointer",transition:"border-color 0.2s ease, background 0.2s ease"},children:[h.jsx("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:12},children:h.jsxs("div",{style:{display:"flex",alignItems:"flex-start",gap:10},children:[h.jsx("div",{style:{width:28,height:28,borderRadius:8,display:"grid",placeItems:"center",fontSize:12,fontWeight:700,color:m?r.accent:"#7d889d",background:m?`${r.accent}18`:"rgba(255,255,255,0.03)",border:`1px solid ${m?`${r.accent}33`:"rgba(255,255,255,0.06)"}`,flexShrink:0},children:u+1}),h.jsxs("div",{children:[h.jsx("div",{style:{fontSize:14,fontWeight:650,color:m?"#eff4ff":"#cbd5e5"},children:r.name}),h.jsx("div",{style:{fontSize:10,letterSpacing:"0.08em",textTransform:"uppercase",color:m?r.accent:"#6d7890",marginTop:4},children:r.useCase})]})]})}),h.jsx("div",{style:{fontSize:11,color:"#7f8aa0",lineHeight:1.55,marginTop:10},children:r.description})]},r.name)})}),h.jsxs("div",{style:{padding:"14px 18px 16px",borderTop:"1px solid rgba(255,255,255,0.06)"},children:[h.jsx("button",{onClick:()=>{var r;return(r=e.current)==null?void 0:r.triggerPulse()},style:{width:"100%",padding:"11px 0",borderRadius:10,border:`1px solid ${c.accent}44`,background:`linear-gradient(135deg, ${c.accent}22 0%, rgba(255,255,255,0.03) 100%)`,color:"#eef4ff",fontSize:12,fontWeight:650,letterSpacing:"0.08em",textTransform:"uppercase",cursor:"pointer"},children:"Trigger event pulse"}),h.jsxs("div",{style:{display:"grid",gap:6,marginTop:12},children:[h.jsxs("div",{style:{fontSize:10,color:"#5f6980",lineHeight:1.5},children:["Drag to orbit, scroll to zoom. Route: ",h.jsx("code",{children:"#/colonization-demo"})]}),h.jsx("div",{style:{fontSize:10,color:"#5f6980",lineHeight:1.5},children:"Suggested product mapping: keep the node field fixed, arm only event-relevant attractors, and let the scaffold persist as a compact memory of recent flow."})]})]})]}),h.jsx("a",{href:"#/three",style:{position:"absolute",top:16,left:16,padding:"7px 12px",borderRadius:999,border:"1px solid rgba(255,255,255,0.08)",background:"rgba(8,10,18,0.55)",color:"#a8b3c8",textDecoration:"none",fontFamily:"'Inter', system-ui, sans-serif",fontSize:11,letterSpacing:"0.04em",backdropFilter:"blur(12px)"},children:"← back to live view"})]})}export{Ce as ColonizationDemoScene};
@@ -0,0 +1,138 @@
1
+ import{C as K,G as L,W as X,S as Y,P as J,O as Q,B as Z,E as ee,R as te,U as oe,V as ae,L as W,a as u,b as H,M as F,A as M,c as S,d as B,e as re,D as ne,T as ie,f as U,g as N,h as E,i as z,j as se,s as le,k as R,r as _,l as g}from"./index-zmiMo1Db.js";const j=new Map;let $=U;function ce(r,t){j.set(r,t),$=(o,i,s)=>{const d=j.get(o);return d?d(i,s):U(o,i,s)}}class ue{constructor(t){this.clock=new K,this.nodeGroup=new L,this.currentNode=null,this.respawnTimer=0,this.currentVariation=null,this.axisGroup=new L,this._onResize=()=>{const i=this.renderer.domElement.parentElement;if(!i)return;const s=i.clientWidth,d=i.clientHeight;this.camera.aspect=s/d,this.camera.updateProjectionMatrix(),this.renderer.setSize(s,d),this.composer.setSize(s,d)},this.renderer=new X({antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.setSize(t.clientWidth,t.clientHeight),this.renderer.setClearColor(197384),t.appendChild(this.renderer.domElement),this.scene=new Y,this.camera=new J(55,t.clientWidth/t.clientHeight,1,2e3),this.camera.position.set(0,60,200),this.controls=new Q(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.06,this.controls.autoRotate=!1,this.controls.target.set(-40,5,-15),this.controls.minDistance=50,this.controls.maxDistance=600,this.background=new Z,this.scene.add(this.background.group),this.scene.add(this.nodeGroup),this.scene.add(this.axisGroup),this._buildAxis(),this._buildCoreGlow(),this.composer=new ee(this.renderer),this.composer.addPass(new te(this.scene,this.camera));const o=new oe(new ae(t.clientWidth,t.clientHeight),.7,.4,.75);this.composer.addPass(o),window.addEventListener("resize",this._onResize),this._onResize()}_buildAxis(){const t=new W({color:2236996,transparent:!0,opacity:.4}),o=(i,s)=>{const d=new N().setFromPoints([i,s]);return new E(d,t)};this.axisGroup.add(o(new u(-120,0,0),new u(20,0,0))),this.axisGroup.add(o(new u(0,-20,0),new u(0,30,0))),this.axisGroup.add(o(new u(0,0,-60),new u(0,0,20)))}_buildCoreGlow(){const t=new H(3,16,16),o=new F({color:new S("#22ff88"),transparent:!0,opacity:.15,blending:M});this.scene.add(new B(t,o));const i=new re(5,6,32),s=new F({color:new S("#4ade80"),transparent:!0,opacity:.2,blending:M,side:ne});this.scene.add(new B(i,s));const d=new u(-80,10,-30),v=new H(2,12,12),h=new F({color:new S("#4ade80"),transparent:!0,opacity:.3,blending:M}),m=new B(v,h);m.position.copy(d),this.scene.add(m)}setVariation(t){if(this.currentVariation=t,t.buildCustomEffect){const o=t.spawnParams()._tool??"";ce(o,t.buildCustomEffect)}this._clearNode(),this._spawnNode()}_clearNode(){this.currentNode&&(this.nodeGroup.remove(this.currentNode.group),this.currentNode.dispose(),this.currentNode=null)}_spawnNode(){if(!this.currentVariation)return;const t=this.currentVariation.spawnParams(this.camera.position),o=new ie(t,$);this.nodeGroup.add(o.group),this.currentNode=o,this.respawnTimer=0}tick(){const t=Math.min(this.clock.getDelta(),.05);this.controls.update(),this.background.tick(t),this.currentNode?(this.currentNode.tick(t),this.currentNode.done&&(this._clearNode(),this.respawnTimer=0)):(this.respawnTimer+=t,this.respawnTimer>1&&this.currentVariation&&this._spawnNode()),this.composer.render()}destroy(){window.removeEventListener("resize",this._onResize),this.renderer.dispose(),this._clearNode(),j.clear()}}const q=new u(0,0,0);function x(r,t){return Math.random()*(t-r)+r}function de(){return{origin:new u(-120,15,-60),target:q.clone().add(new u(x(-2,2),x(-2,2),x(-2,2))),color:new S("#b0c8f0"),colorEnd:new S("#ffffff"),travelTime:3,trailLength:0,trailBrightness:0,trailFadeTime:4,headSize:0,pathConfig:{type:"linear",easing:"easeInCubic"},_tool:"prompt:custom"}}function pe(){const r=Math.random()*Math.PI*2,t=Math.acos(x(-1,1)),o=x(250,400);return new u(Math.sin(t)*Math.cos(r)*o,Math.sin(t)*Math.sin(r)*o,Math.cos(t)*o)}function O(r){const t=de();t._tool=r,t.origin=pe(),t.travelTime=x(4,5.5),t.trailFadeTime=1.8;const o=t.origin.clone().add(q).multiplyScalar(.5);return t.pathConfig={type:"quadratic",easing:"easeInOut",c1:new u(o.x+x(-40,40),o.y+x(20,60),o.z+x(-30,30))},t}function he(r,t){return{f1:1.2+Math.random()*1.5,f2:3.5+Math.random()*3,ph1:Math.random()*Math.PI*2,ph2:Math.random()*Math.PI*2,amp:4+Math.random()*6,perp:new u(Math.random()-.5,Math.random()-.5,Math.random()-.5).normalize(),scatter:new u((Math.random()-.5)*400,(Math.random()-.5)*400,(Math.random()-.5)*400),windowStart:-(r/t)*3.5-Math.random()*.2,rollSpeed:.8+Math.random()*.6}}function fe(r,t,o,i,s){const d=se(t.pathConfig.easing,o),v=le(t.pathConfig,t.origin,t.target,d),h=Math.max(0,1-o/.5),m=Math.min(1,o/.5),l=1-Math.pow(o,8),e=(Math.sin(o*r.f1*Math.PI*2+r.ph1+i*s)*r.amp+Math.sin(o*r.f2*Math.PI*2+r.ph2+i*s*1.4)*r.amp*.3)*m*l;return new u(v.x+r.scatter.x*h+r.perp.x*e,v.y+r.scatter.y*h+r.perp.y*e,v.z+r.scatter.z*h+r.perp.z*e)}function P(r,t,o,i,s){const d=Array.from({length:i},(h,m)=>{const l=he(m,i),e=new Float32Array(o*3),n=new N;n.setAttribute("position",new z(e,3));const c=new Float32Array(o);for(let T=0;T<o;T++)c[T]=T/(o-1);n.setAttribute("aT",new z(c,1));const{mat:a,setOpacity:p,setTime:w}=s(m,n);return r.add(new E(n,a)),{...l,posArr:e,geo:n,mat:a,window:l.windowStart,setOpacity:p,setTime:w}}),v={entries:d.map(h=>({obj:new E(h.geo,h.mat),mat:h.mat,meta:{}})),suppressDefaultTrail:!0,trailTick(h,m,l){var c;const e=2.5+l*.1;let n=!0;for(const a of d){const p=a.window>=1?1/(1.5*a.rollSpeed):(1-a.windowStart)/t.travelTime;a.window=Math.min(2,a.window+m*p);const w=a.window;if(w<=0){a.geo.setDrawRange(0,0),n=!1;continue}if(w>=2){a.setOpacity(0),a.geo.setDrawRange(0,0);continue}n=!1;const T=Math.round(Math.min(1,w)*(o-1)),y=Math.round(Math.max(0,w-1)*(o-1)),A=T-y;if(A<1){a.geo.setDrawRange(0,0);continue}for(let b=y;b<=T;b++){const C=fe(a,t,b/(o-1),l,e);a.posArr[b*3]=C.x,a.posArr[b*3+1]=C.y,a.posArr[b*3+2]=C.z}a.geo.attributes.position.needsUpdate=!0,a.geo.setDrawRange(y,A),a.setOpacity(.7),(c=a.setTime)==null||c.call(a,l)}n&&(v.shouldMarkDone=!0)},tick(){},onLand(){},applyFade(h){},shouldMarkDone:!1};return v}function ge(r,t){return P(r,t,120,8,o=>{const i=new W({color:new S().setHSL(.55+o/8*.15,.9,.6+o%2*.2),transparent:!0,opacity:0,blending:M,depthWrite:!1});return{mat:i,setOpacity:s=>{i.opacity=s}}})}const me=`
2
+ attribute float aT;
3
+ varying float vT;
4
+ void main() { vT = aT; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
5
+ `,ve=`
6
+ uniform float uTime;
7
+ uniform float uOpacity;
8
+ uniform float uHueBase;
9
+ varying float vT;
10
+ vec3 hsl2rgb(float h, float s, float l) {
11
+ h = mod(h, 1.0);
12
+ vec3 rgb = clamp(abs(mod(h*6.0+vec3(0,4,2),6.0)-3.0)-1.0, 0.0, 1.0);
13
+ return l + s*(rgb-0.5)*(1.0-abs(2.0*l-1.0));
14
+ }
15
+ void main() {
16
+ // Hue drifts along length + time
17
+ float hue = mod(uHueBase + vT * 0.3 + uTime * 0.12, 1.0);
18
+ float core = pow(1.0 - vT, 0.3); // bright at head (t=1 is target)
19
+ float bloom = pow(vT, 0.5) * 0.6;
20
+ vec3 col = hsl2rgb(hue, 1.0, 0.55 + bloom * 0.3);
21
+ // White-hot center
22
+ col = mix(col, vec3(1.0), core * 0.4);
23
+ float alpha = (bloom + core * 0.5) * uOpacity;
24
+ gl_FragColor = vec4(col * (bloom + core * 0.5 + 0.05), alpha);
25
+ }
26
+ `;function we(r,t){return P(r,t,120,8,o=>{const i=new R({vertexShader:me,fragmentShader:ve,uniforms:{uTime:{value:0},uOpacity:{value:0},uHueBase:{value:o/8}},blending:M,transparent:!0,depthWrite:!1});return{mat:i,setOpacity:s=>{i.uniforms.uOpacity.value=s},setTime:s=>{i.uniforms.uTime.value=s}}})}const be=`
27
+ attribute float aT;
28
+ varying float vT;
29
+ void main() { vT = aT; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
30
+ `,Te=`
31
+ uniform float uTime;
32
+ uniform float uOpacity;
33
+ uniform float uPhase;
34
+ varying float vT;
35
+ float hash(float n) { return fract(sin(n)*43758.5453); }
36
+ void main() {
37
+ // High-freq flicker noise
38
+ float noise = hash(floor(vT * 80.0 + uTime * 30.0));
39
+ float flicker = 0.7 + 0.3 * noise;
40
+ // Traveling pulse toward core (vT=1)
41
+ float pulsePos = mod(uPhase + uTime * 0.6, 1.0);
42
+ float pulse = exp(-pow((vT - pulsePos) / 0.05, 2.0)) * 2.5;
43
+ // Base glow — brighter near core
44
+ float base = pow(vT, 0.4) * 0.5;
45
+ float b = (base + pulse) * flicker;
46
+ // Color: deep blue → cyan → white at pulse peak
47
+ vec3 blue = vec3(0.05, 0.2, 1.0);
48
+ vec3 cyan = vec3(0.2, 0.9, 1.0);
49
+ vec3 white = vec3(1.0, 1.0, 1.0);
50
+ vec3 col = mix(blue, cyan, vT);
51
+ col = mix(col, white, clamp(pulse / 2.5, 0.0, 1.0));
52
+ gl_FragColor = vec4(col * b, b * uOpacity);
53
+ }
54
+ `;function ye(r,t){return P(r,t,120,8,o=>{const i=new R({vertexShader:be,fragmentShader:Te,uniforms:{uTime:{value:0},uOpacity:{value:0},uPhase:{value:o/8}},blending:M,transparent:!0,depthWrite:!1});return{mat:i,setOpacity:s=>{i.uniforms.uOpacity.value=s},setTime:s=>{i.uniforms.uTime.value=s}}})}const xe=`
55
+ attribute float aT;
56
+ varying float vT;
57
+ void main() { vT = aT; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
58
+ `,Me=`
59
+ uniform float uTime;
60
+ uniform float uOpacity;
61
+ uniform float uOffset;
62
+ varying float vT;
63
+ vec3 hsl2rgb(float h, float s, float l) {
64
+ h = mod(h, 1.0);
65
+ vec3 rgb = clamp(abs(mod(h*6.0+vec3(0,4,2),6.0)-3.0)-1.0, 0.0, 1.0);
66
+ return l + s*(rgb-0.5)*(1.0-abs(2.0*l-1.0));
67
+ }
68
+ void main() {
69
+ // Slow drifting color bands along the strand
70
+ float hue = mod(uOffset + vT * 0.5 + uTime * 0.04, 1.0);
71
+ float bright = 0.5 + 0.5 * sin(vT * 3.0 + uTime * 0.8 + uOffset * 6.28);
72
+ // Soft curtain falloff — bright in the middle of length
73
+ float curtain = sin(vT * 3.14159) * 0.8 + 0.2;
74
+ // Very soft, high luminance pastels
75
+ vec3 col = hsl2rgb(hue, 0.6, 0.6 + bright * 0.25);
76
+ float alpha = curtain * bright * 0.5 * uOpacity;
77
+ gl_FragColor = vec4(col * curtain * bright, alpha);
78
+ }
79
+ `;function Se(r,t){return P(r,t,120,10,o=>{const i=new R({vertexShader:xe,fragmentShader:Me,uniforms:{uTime:{value:0},uOpacity:{value:0},uOffset:{value:o/10}},blending:M,transparent:!0,depthWrite:!1});return{mat:i,setOpacity:s=>{i.uniforms.uOpacity.value=s},setTime:s=>{i.uniforms.uTime.value=s}}})}const Ae=`
80
+ attribute float aT;
81
+ varying float vT;
82
+ void main() { vT = aT; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
83
+ `,Ce=`
84
+ uniform float uTime;
85
+ uniform float uOpacity;
86
+ uniform float uHue;
87
+ varying float vT;
88
+ vec3 hsl2rgb(float h, float s, float l) {
89
+ h = mod(h, 1.0);
90
+ vec3 rgb = clamp(abs(mod(h*6.0+vec3(0,4,2),6.0)-3.0)-1.0, 0.0, 1.0);
91
+ return l + s*(rgb-0.5)*(1.0-abs(2.0*l-1.0));
92
+ }
93
+ void main() {
94
+ // Traveling energy packets
95
+ float packet1 = exp(-pow(mod(vT - uTime * 0.5, 1.0) / 0.08, 2.0));
96
+ float packet2 = exp(-pow(mod(vT - uTime * 0.5 + 0.4, 1.0) / 0.08, 2.0));
97
+ float energy = packet1 + packet2 * 0.6;
98
+ // White-hot spine at high energy, colored bleed otherwise
99
+ vec3 fiberCol = hsl2rgb(uHue, 1.0, 0.5);
100
+ vec3 col = mix(fiberCol, vec3(1.0), clamp(energy, 0.0, 1.0));
101
+ // Base thread glow — constant soft light
102
+ float base = 0.2 + 0.1 * sin(vT * 20.0 - uTime * 3.0);
103
+ float alpha = (base + energy * 0.8) * uOpacity;
104
+ gl_FragColor = vec4(col * (base + energy * 0.8), alpha);
105
+ }
106
+ `;function _e(r,t){return P(r,t,120,8,o=>{const i=new R({vertexShader:Ae,fragmentShader:Ce,uniforms:{uTime:{value:0},uOpacity:{value:0},uHue:{value:.55+o/8*.35}},blending:M,transparent:!0,depthWrite:!1});return{mat:i,setOpacity:s=>{i.uniforms.uOpacity.value=s},setTime:s=>{i.uniforms.uTime.value=s}}})}const ke=[{name:"Writhing",description:"Baseline — colored strands scatter, converge, weave into core",spawnParams:()=>O("prompt:writhing"),buildCustomEffect:ge},{name:"Neon Iridescent",description:"Hue shifts along length and over time — white-hot core bloom",spawnParams:()=>O("prompt:neon"),buildCustomEffect:we},{name:"Plasma",description:"Electric blue-white with noisy flicker and traveling pulses",spawnParams:()=>O("prompt:plasma"),buildCustomEffect:ye},{name:"Aurora",description:"Soft pastel curtains drifting with slow color bands",spawnParams:()=>O("prompt:aurora"),buildCustomEffect:Se},{name:"Fiber Light",description:"White-hot spine with colored energy packets traveling to core",spawnParams:()=>O("prompt:fiber"),buildCustomEffect:_e}];function f(r,t){return Math.random()*(t-r)+r}const Oe=new u(0,0,0);function Re(){const r=Math.random()*Math.PI*2,t=f(60,110),o=new u(Math.cos(r)*t,f(-8,15),Math.sin(r)*t*.5),i=o.clone().multiplyScalar(.5);return{origin:Oe.clone(),target:o,color:new S("#a78bfa"),colorEnd:new S("#e9d5ff"),travelTime:f(2.5,3.5),trailLength:0,trailBrightness:0,trailFadeTime:30,headSize:0,pathConfig:{type:"quadratic",easing:"easeInOut",c1:new u(i.x+f(-25,25),i.y+f(10,35),i.z+f(-20,20))},_tool:"Glob:demo"}}const V=80,I=10,Pe=.35,Ee=`
107
+ attribute float aT;
108
+ varying float vT;
109
+ void main() {
110
+ vT = aT;
111
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
112
+ }
113
+ `,ze=`
114
+ uniform float uTime;
115
+ uniform float uOpacity;
116
+ uniform vec3 uBaseColor;
117
+ uniform vec3 uTipColor;
118
+ uniform float uPhase;
119
+ varying float vT;
120
+
121
+ void main() {
122
+ vec3 col = mix(uBaseColor, uTipColor, vT);
123
+
124
+ // energy flow racing toward tip
125
+ float flow = sin((vT * 8.0 - uTime * 2.0 + uPhase) * 3.14159);
126
+ flow = pow(max(0.0, flow), 3.0) * 0.3;
127
+ col += uTipColor * flow;
128
+
129
+ // hot head: last 10% glows bright
130
+ float head = smoothstep(0.9, 1.0, vT);
131
+ col = mix(col, uTipColor * 1.5, head * 0.7);
132
+
133
+ float bright = 0.4 + vT * 0.3 + flow * 0.3 + head * 0.8;
134
+ float alpha = uOpacity * (0.3 + vT * 0.3 + flow * 0.15 + head * 0.4);
135
+
136
+ gl_FragColor = vec4(min(col * bright, 1.0), min(alpha, 1.0));
137
+ }
138
+ `;function De(r,t){const o=[];function i(e,n){const c=f(0,Math.PI*2),a=f(-.6,.6),p=new u(Math.cos(c)*Math.cos(a),Math.sin(a),Math.sin(c)*Math.cos(a)).normalize(),w=f(14,22),T=p.clone().multiplyScalar(w),y=new Float32Array(V*3),A=new Float32Array(V),b=new N;b.setAttribute("position",new z(y,3)),b.setAttribute("aT",new z(A,1)),b.setDrawRange(0,0);const C=new R({vertexShader:Ee,fragmentShader:ze,uniforms:{uTime:{value:0},uOpacity:{value:.85},uBaseColor:{value:new u(.2,.12,.4)},uTipColor:{value:new u(.5,.35,.9)},uPhase:{value:f(0,6.28)}},blending:M,transparent:!0,depthWrite:!1}),D=new E(b,C);D.visible=!1,r.add(D),y[0]=t.target.x,y[1]=t.target.y,y[2]=t.target.z,A[0]=0;const G={origin:t.target.clone(),isHit:n,fireDelay:e,pos:t.target.clone(),vel:T,searchDist:f(20,45),gravity:new u(f(-1.5,1.5),f(-3,-1),f(-1.5,1.5)),drift:new u(f(-2,2),f(-.5,.5),f(-2,2)),returnStart:new u,returnArc:new u,returnT:0,returnDur:f(1.2,2),points:[t.target.clone()],posArr:y,tArr:A,geo:b,mat:C,line:D,ptCount:1,state:"waiting",elapsed:0,stateTime:0,tailIdx:0};return o.push(G),G}const s=[],d=Array.from({length:I},(e,n)=>n);for(let e=d.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[d[e],d[n]]=[d[n],d[e]]}for(let e=0;e<I;e++){const n=d[e]<I*Pe;s.push(i(f(.1,3),n))}function v(e){const n=Math.min(e.points.length,V),c=e.points.length-n;for(let a=0;a<n;a++){const p=e.points[c+a];e.posArr[a*3]=p.x,e.posArr[a*3+1]=p.y,e.posArr[a*3+2]=p.z,e.tArr[a]=a/(n-1||1)}e.ptCount=n,e.geo.attributes.position.needsUpdate=!0,e.geo.attributes.aT.needsUpdate=!0}function h(e,n,c,a){const p=1-a;return new u(p*p*e.x+2*p*a*n.x+a*a*c.x,p*p*e.y+2*p*a*n.y+a*a*c.y,p*p*e.z+2*p*a*n.z+a*a*c.z)}function m(e,n){if(e.elapsed+=n,e.mat.uniforms.uTime.value=e.elapsed,e.state==="waiting"){e.elapsed>=e.fireDelay&&(e.state="seeking",e.stateTime=0,e.line.visible=!0);return}if(e.stateTime+=n,e.state==="seeking"){if(e.vel.addScaledVector(e.gravity,n),e.vel.addScaledVector(e.drift,n),e.vel.multiplyScalar(1-n*.3),e.pos.addScaledVector(e.vel,n),e.points.push(e.pos.clone()),v(e),e.geo.setDrawRange(0,e.ptCount),e.pos.distanceTo(e.origin)>=e.searchDist)if(e.isHit){e.state="hit_returning",e.stateTime=0,e.returnStart.copy(e.pos);const c=e.pos.clone().add(e.origin).multiplyScalar(.5);e.returnArc.set(c.x+f(-18,18),c.y+f(8,25),c.z+f(-18,18)),e.returnT=0,e.mat.uniforms.uBaseColor.value.set(.05,.35,.15),e.mat.uniforms.uTipColor.value.set(.3,1,.5)}else e.state="miss_dying",e.stateTime=0,e.tailIdx=0,e.mat.uniforms.uBaseColor.value.set(.3,.05,.05),e.mat.uniforms.uTipColor.value.set(.5,.1,.08)}else if(e.state==="hit_returning"){e.returnT=Math.min(1,e.returnT+n/e.returnDur);const c=e.returnT<.5?2*e.returnT*e.returnT:1-Math.pow(-2*e.returnT+2,2)/2;e.pos.copy(h(e.returnStart,e.returnArc,e.origin,c)),e.points.push(e.pos.clone()),v(e),e.geo.setDrawRange(0,e.ptCount),e.returnT>=1&&(e.state="miss_dying",e.stateTime=0,e.tailIdx=0)}else if(e.state==="miss_dying"){const c=e.isHit?25:15;e.tailIdx=Math.min(e.ptCount-1,e.tailIdx+n*c);const a=Math.round(e.tailIdx),p=e.ptCount-a;if(p<=1){e.state="done",e.geo.setDrawRange(0,0),e.line.visible=!1;return}e.geo.setDrawRange(a,p);for(let w=a;w<e.ptCount;w++)e.tArr[w]=(w-a)/(p-1);e.geo.attributes.aT.needsUpdate=!0,e.isHit||(e.mat.uniforms.uOpacity.value=Math.max(0,.85-e.stateTime*.4))}}const l={entries:[],suppressDefaultTrail:!0,trailTick(e,n){for(const c of s)m(c,n);o.every(c=>c.state==="done")&&(l.shouldMarkDone=!0)},tick(){},onLand(){},applyFade(e){},shouldMarkDone:!1,dispose(){for(const e of o)e.geo.dispose(),e.mat.dispose()}};return l}const Fe=[{name:"Seeker Missiles",description:"Missiles launch staggered, seek outward; hits glow green and arc back, misses turn red and dissolve",spawnParams:Re,buildCustomEffect:De}],k=[{label:"Prompt",color:"#93c5fd",variations:ke},{label:"Glob",color:"#a78bfa",variations:Fe}];function Ve(){const r=_.useRef(null),t=_.useRef(null),o=_.useRef(0),[i,s]=_.useState(0),[d,v]=_.useState(0),h=k[i];_.useEffect(()=>{if(!r.current)return;const l=new ue(r.current);t.current=l;const e=k[0].variations[0];l.setVariation(e);function n(){l.tick(),o.current=requestAnimationFrame(n)}return o.current=requestAnimationFrame(n),()=>{cancelAnimationFrame(o.current),l.destroy(),t.current=null}},[]);function m(l,e){var c;s(l),v(e);const n=k[l].variations[e];(c=t.current)==null||c.setVariation(n)}return g.jsxs("div",{style:{width:"100vw",height:"100vh",background:"#030308",display:"flex",position:"relative"},children:[g.jsx("div",{ref:r,style:{flex:1,height:"100%"}}),g.jsxs("div",{style:{position:"absolute",top:0,right:0,width:280,height:"100%",background:"rgba(3,3,14,0.88)",borderLeft:"1px solid rgba(255,255,255,0.06)",display:"flex",flexDirection:"column",fontFamily:"monospace",color:"#aaa",overflowY:"auto"},children:[g.jsxs("div",{style:{padding:"16px 16px 10px",borderBottom:"1px solid rgba(255,255,255,0.06)"},children:[g.jsx("div",{style:{fontSize:11,letterSpacing:"0.1em",color:"#555",marginBottom:4},children:"EFFECT DEMO"}),g.jsx("div",{style:{fontSize:13,color:"#eee"},children:"trail variations"})]}),g.jsx("div",{style:{display:"flex",padding:"10px 10px 0",gap:6,flexWrap:"wrap"},children:k.map((l,e)=>g.jsx("button",{onClick:()=>m(e,0),style:{padding:"4px 10px",fontSize:11,borderRadius:3,border:`1px solid ${i===e?l.color:"rgba(255,255,255,0.1)"}`,background:i===e?`${l.color}18`:"transparent",color:i===e?l.color:"#666",cursor:"pointer",letterSpacing:"0.05em"},children:l.label},l.label))}),g.jsx("div",{style:{padding:10,display:"flex",flexDirection:"column",gap:8,flex:1},children:h.variations.map((l,e)=>{const n=i===k.indexOf(h)&&d===e;return g.jsxs("button",{onClick:()=>m(k.indexOf(h),e),style:{textAlign:"left",padding:"10px 12px",borderRadius:5,border:`1px solid ${n?h.color:"rgba(255,255,255,0.07)"}`,background:n?`${h.color}12`:"rgba(255,255,255,0.02)",cursor:"pointer",color:"inherit",transition:"border-color 0.15s"},children:[g.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4},children:[g.jsx("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:n?h.color:"#444",flexShrink:0}}),g.jsxs("span",{style:{fontSize:12,color:n?h.color:"#ccc",fontWeight:600},children:[e+1,". ",l.name]})]}),g.jsx("div",{style:{fontSize:11,color:"#666",lineHeight:1.5,paddingLeft:16},children:l.description})]},l.name)})}),g.jsx("div",{style:{padding:"10px 16px",borderTop:"1px solid rgba(255,255,255,0.05)",fontSize:10,color:"#444"},children:"drag to orbit · scroll to zoom · node loops automatically"})]}),g.jsx("a",{href:"#/three",style:{position:"absolute",top:14,left:14,fontSize:11,color:"#444",textDecoration:"none",fontFamily:"monospace",letterSpacing:"0.05em"},children:"← back"})]})}export{Ve as EffectDemoScene};