helixmind 0.2.29 → 0.2.30

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.
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/cli/brain/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAqrE3D"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/cli/brain/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA0/D3D"}
@@ -593,196 +593,17 @@ const EDGE_COLORS = {
593
593
  belongs_to: '#ff6600', part_of: '#ff6600', supersedes: '#ff4444',
594
594
  default: '#334466',
595
595
  };
596
- // V7: Clustered Galaxy Brain Force-directed with level clustering
597
- // Each level forms an organic morphed blob; blobs barely touch each other
598
- const LEVEL_STYLE = {
599
- 1: { size: 22, pulse: 2.0, activity: 1.0 },
600
- 2: { size: 20, pulse: 1.5, activity: 0.85 },
601
- 3: { size: 17, pulse: 1.0, activity: 0.7 },
602
- 4: { size: 15, pulse: 0.6, activity: 0.5 },
603
- 5: { size: 13, pulse: 0.3, activity: 0.3 },
604
- 6: { size: 18, pulse: 0.8, activity: 0.7 },
596
+ // Organic nebula layoutlayers spread wide and overlap slightly
597
+ // Not a strict funnel but an organic cloud where each layer bleeds into neighbors
598
+ const SPATIAL = {
599
+ 5: { iR: 5, oR: 90, yBase: 450, yS: 120, size: 52, pulse: 0.3 },
600
+ 4: { iR: 15, oR: 180, yBase: 280, yS: 130, size: 42, pulse: 0.5 },
601
+ 3: { iR: 30, oR: 280, yBase: 100, yS: 140, size: 36, pulse: 0.8 },
602
+ 2: { iR: 50, oR: 380, yBase: -90, yS: 150, size: 28, pulse: 1.2 },
603
+ 1: { iR: 70, oR: 480, yBase: -280, yS: 160, size: 22, pulse: 2.0 },
604
+ 6: { iR: 90, oR: 580, yBase: -480, yS: 170, size: 30, pulse: 0.6 },
605
605
  };
606
- const MAX_RENDERED_EDGES = 12000; // more edges visible in galaxy layout
607
-
608
- // Clustered force-directed layout: each level forms an organic blob
609
- // Blobs are pushed apart so they barely touch — morphed spheres
610
- function computeForceLayout(nodeList, edgeList, nodeIdxMapLocal) {
611
- const N = nodeList.length;
612
- if (N === 0) return [];
613
-
614
- // Group nodes by level
615
- const levelGroups = {};
616
- for (let i = 0; i < N; i++) {
617
- const lv = nodeList[i].level || 3;
618
- if (!levelGroups[lv]) levelGroups[lv] = [];
619
- levelGroups[lv].push(i);
620
- }
621
-
622
- // Assign each level a seed centroid direction (icosahedron-like placement)
623
- const levels = Object.keys(levelGroups).map(Number).sort();
624
- const seedCentroids = {};
625
- const CLUSTER_SPREAD = 320;
626
- // Evenly space level centroids on a sphere
627
- for (let li = 0; li < levels.length; li++) {
628
- const lv = levels[li];
629
- const golden = 2.399963;
630
- const theta = golden * li * 2.5; // multiplied for wider spread
631
- const phi = Math.acos(1 - 2 * (li + 0.5) / Math.max(levels.length, 2));
632
- seedCentroids[lv] = {
633
- x: CLUSTER_SPREAD * Math.sin(phi) * Math.cos(theta),
634
- y: CLUSTER_SPREAD * Math.cos(phi),
635
- z: CLUSTER_SPREAD * Math.sin(phi) * Math.sin(theta)
636
- };
637
- }
638
-
639
- const pos = new Array(N);
640
- // Initialize nodes near their level's seed centroid
641
- for (let i = 0; i < N; i++) {
642
- const lv = nodeList[i].level || 3;
643
- const c = seedCentroids[lv] || { x: 0, y: 0, z: 0 };
644
- const INIT_SPREAD = 100;
645
- pos[i] = {
646
- x: c.x + (srand(i * 7) - 0.5) * INIT_SPREAD,
647
- y: c.y + (srand(i * 13) - 0.5) * INIT_SPREAD,
648
- z: c.z + (srand(i * 19) - 0.5) * INIT_SPREAD
649
- };
650
- }
651
-
652
- // Build adjacency
653
- const adjList = new Array(N);
654
- for (let i = 0; i < N; i++) adjList[i] = [];
655
- for (const e of edgeList) {
656
- const si = nodeIdxMapLocal[e.source];
657
- const ti = nodeIdxMapLocal[e.target];
658
- if (si !== undefined && ti !== undefined) {
659
- adjList[si].push(ti);
660
- adjList[ti].push(si);
661
- }
662
- }
663
-
664
- const ITERATIONS = 60;
665
- const REPULSION = 5000;
666
- const ATTRACTION = 0.012;
667
- const CLUSTER_PULL = 0.02; // pull nodes toward their own level centroid
668
- const INTER_REPEL = 18000; // push different-level centroids apart
669
- const CENTERING = 0.0005;
670
- const DAMPING = 0.82;
671
- const K_SAMPLES = Math.min(N, 25);
672
-
673
- const vel = new Array(N);
674
- for (let i = 0; i < N; i++) vel[i] = { x: 0, y: 0, z: 0 };
675
-
676
- for (let iter = 0; iter < ITERATIONS; iter++) {
677
- const temp = 1.0 - iter / ITERATIONS;
678
- const repScale = REPULSION * temp;
679
-
680
- // Compute dynamic level centroids
681
- const dynC = {};
682
- const dynN = {};
683
- for (let i = 0; i < N; i++) {
684
- const lv = nodeList[i].level || 3;
685
- if (!dynC[lv]) { dynC[lv] = { x: 0, y: 0, z: 0 }; dynN[lv] = 0; }
686
- dynC[lv].x += pos[i].x; dynC[lv].y += pos[i].y; dynC[lv].z += pos[i].z;
687
- dynN[lv]++;
688
- }
689
- for (const lv in dynC) {
690
- dynC[lv].x /= dynN[lv]; dynC[lv].y /= dynN[lv]; dynC[lv].z /= dynN[lv];
691
- }
692
-
693
- for (let i = 0; i < N; i++) {
694
- let fx = 0, fy = 0, fz = 0;
695
- const myLevel = nodeList[i].level || 3;
696
-
697
- // Node-node repulsion (sampled)
698
- for (let k = 0; k < K_SAMPLES; k++) {
699
- const j = Math.floor(srand(iter * 10007 + i * 997 + k * 31) * N);
700
- if (j === i) continue;
701
- const dx = pos[i].x - pos[j].x;
702
- const dy = pos[i].y - pos[j].y;
703
- const dz = pos[i].z - pos[j].z;
704
- const distSq = dx * dx + dy * dy + dz * dz + 1;
705
- // Stronger repulsion between different levels
706
- const crossMult = (nodeList[j].level || 3) !== myLevel ? 2.5 : 1.0;
707
- const f = repScale * crossMult / distSq;
708
- const dist = Math.sqrt(distSq);
709
- fx += (dx / dist) * f; fy += (dy / dist) * f; fz += (dz / dist) * f;
710
- }
711
- const repBias = N / K_SAMPLES;
712
- fx *= repBias; fy *= repBias; fz *= repBias;
713
-
714
- // Attraction: pull toward connected nodes (stronger for same level)
715
- for (const j of adjList[i]) {
716
- const dx = pos[j].x - pos[i].x;
717
- const dy = pos[j].y - pos[i].y;
718
- const dz = pos[j].z - pos[i].z;
719
- const dist = Math.sqrt(dx * dx + dy * dy + dz * dz + 1);
720
- const sameLvl = (nodeList[j].level || 3) === myLevel ? 2.5 : 0.3;
721
- const f = ATTRACTION * dist * sameLvl;
722
- fx += (dx / dist) * f; fy += (dy / dist) * f; fz += (dz / dist) * f;
723
- }
724
-
725
- // Cluster cohesion: pull toward own level centroid
726
- const mc = dynC[myLevel];
727
- if (mc) {
728
- fx += (mc.x - pos[i].x) * CLUSTER_PULL * temp;
729
- fy += (mc.y - pos[i].y) * CLUSTER_PULL * temp;
730
- fz += (mc.z - pos[i].z) * CLUSTER_PULL * temp;
731
- }
732
-
733
- // Inter-cluster repulsion: push away from OTHER level centroids
734
- for (const lv in dynC) {
735
- if (parseInt(lv) === myLevel) continue;
736
- const oc = dynC[lv];
737
- const dx = pos[i].x - oc.x;
738
- const dy = pos[i].y - oc.y;
739
- const dz = pos[i].z - oc.z;
740
- const distSq = dx * dx + dy * dy + dz * dz + 1;
741
- const f = INTER_REPEL * temp / distSq;
742
- const dist = Math.sqrt(distSq);
743
- fx += (dx / dist) * f; fy += (dy / dist) * f; fz += (dz / dist) * f;
744
- }
745
-
746
- // Light centering
747
- fx -= pos[i].x * CENTERING;
748
- fy -= pos[i].y * CENTERING;
749
- fz -= pos[i].z * CENTERING;
750
-
751
- vel[i].x = (vel[i].x + fx) * DAMPING;
752
- vel[i].y = (vel[i].y + fy) * DAMPING;
753
- vel[i].z = (vel[i].z + fz) * DAMPING;
754
-
755
- const maxV = 30 * temp + 2;
756
- const vLen = Math.sqrt(vel[i].x * vel[i].x + vel[i].y * vel[i].y + vel[i].z * vel[i].z);
757
- if (vLen > maxV) {
758
- vel[i].x = vel[i].x / vLen * maxV;
759
- vel[i].y = vel[i].y / vLen * maxV;
760
- vel[i].z = vel[i].z / vLen * maxV;
761
- }
762
- }
763
-
764
- for (let i = 0; i < N; i++) {
765
- pos[i].x += vel[i].x;
766
- pos[i].y += vel[i].y;
767
- pos[i].z += vel[i].z;
768
- }
769
- }
770
-
771
- // Scale to fit (target radius ~650 for spacious clusters)
772
- let maxDist = 0;
773
- for (let i = 0; i < N; i++) {
774
- const d = Math.sqrt(pos[i].x * pos[i].x + pos[i].y * pos[i].y + pos[i].z * pos[i].z);
775
- if (d > maxDist) maxDist = d;
776
- }
777
- const scale = maxDist > 0 ? 650 / maxDist : 1;
778
- for (let i = 0; i < N; i++) {
779
- pos[i].x *= scale;
780
- pos[i].y *= scale;
781
- pos[i].z *= scale;
782
- }
783
-
784
- return pos;
785
- }
606
+ const MAX_RENDERED_EDGES = 8000; // cap for performance + clarity
786
607
 
787
608
  function srand(s) { const x = Math.sin(s * 9301 + 49297) * 49297; return x - Math.floor(x); }
788
609
  function escapeHtml(s) { return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
@@ -795,40 +616,35 @@ document.body.prepend(renderer.domElement);
795
616
 
796
617
  const scene = new THREE.Scene();
797
618
  scene.background = new THREE.Color('#030308');
798
- scene.fog = new THREE.FogExp2('#030308', 0.00018);
619
+ scene.fog = new THREE.FogExp2('#030308', 0.00025);
799
620
 
800
621
  const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 12000);
801
- camera.position.set(900, 500, 900);
622
+ camera.position.set(500, 500, 800);
802
623
 
803
624
  const controls = new OrbitControls(camera, renderer.domElement);
804
625
  controls.target.set(0, 0, 0);
805
626
  controls.enableDamping = true;
806
627
  controls.dampingFactor = 0.06;
807
628
  controls.autoRotate = true;
808
- controls.autoRotateSpeed = 0.12;
629
+ controls.autoRotateSpeed = 0.08;
809
630
  controls.minDistance = 80;
810
631
  controls.maxDistance = 4000;
632
+ controls.maxPolarAngle = Math.PI * 0.85;
633
+ controls.minPolarAngle = Math.PI * 0.15;
811
634
  controls.update();
812
635
 
813
636
  // =========== BACKGROUND STARS ===========
814
- const starCount = 1200;
637
+ const starCount = 700;
815
638
  const starPos = new Float32Array(starCount * 3);
816
- const starCol = new Float32Array(starCount * 3);
817
- const starTc = new THREE.Color();
818
639
  for (let i = 0; i < starCount; i++) {
819
- starPos[i * 3] = (srand(i * 31) - 0.5) * 6000;
820
- starPos[i * 3 + 1] = (srand(i * 37) - 0.5) * 6000;
821
- starPos[i * 3 + 2] = (srand(i * 41) - 0.5) * 6000;
822
- // Subtle color variation in stars
823
- const hue = srand(i * 53) * 0.15 + 0.55; // blue-ish range
824
- starTc.setHSL(hue, 0.3 + srand(i * 59) * 0.3, 0.25 + srand(i * 61) * 0.15);
825
- starCol[i * 3] = starTc.r; starCol[i * 3 + 1] = starTc.g; starCol[i * 3 + 2] = starTc.b;
640
+ starPos[i * 3] = (srand(i * 31) - 0.5) * 5000;
641
+ starPos[i * 3 + 1] = (srand(i * 37) - 0.5) * 5000;
642
+ starPos[i * 3 + 2] = (srand(i * 41) - 0.5) * 5000;
826
643
  }
827
644
  const starGeo = new THREE.BufferGeometry();
828
645
  starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3));
829
- starGeo.setAttribute('color', new THREE.BufferAttribute(starCol, 3));
830
646
  const starMat = new THREE.PointsMaterial({
831
- size: 1.5, vertexColors: true, transparent: true, opacity: 0.6,
647
+ size: 1.2, color: '#223344', transparent: true, opacity: 0.5,
832
648
  blending: THREE.AdditiveBlending, depthWrite: false, sizeAttenuation: true
833
649
  });
834
650
  scene.add(new THREE.Points(starGeo, starMat));
@@ -841,19 +657,14 @@ const nodeMat = new THREE.ShaderMaterial({
841
657
  attribute vec3 aColor;
842
658
  attribute float aHighlight;
843
659
  attribute float aPulse;
844
- attribute float aActivity;
845
660
  varying vec3 vColor;
846
661
  varying float vAlpha;
847
- varying float vActivity;
848
662
  uniform float uTime;
849
663
  void main(){
850
664
  vColor = aColor;
851
- vActivity = aActivity;
852
- float breath = 1.0 + sin(uTime * aPulse + position.x * .008 + position.z * .006) * (.06 + aActivity * .10);
665
+ float breath = 1.0 + sin(uTime * aPulse + position.x * .008 + position.z * .006) * .12;
853
666
  vec3 pos = position;
854
- pos.y += sin(uTime * (.3 + aActivity * .2) + position.x * .01 + position.z * .015) * (2.0 + aActivity * 3.0);
855
- pos.x += sin(uTime * .08 + position.z * .003) * aActivity * 2.0;
856
- pos.z += cos(uTime * .06 + position.x * .003) * aActivity * 2.0;
667
+ pos.y += sin(uTime * .3 + position.x * .01 + position.z * .015) * 3.0;
857
668
  vAlpha = aHighlight;
858
669
  vec4 mv = modelViewMatrix * vec4(pos, 1.0);
859
670
  gl_PointSize = aSize * breath * aHighlight * (500.0 / -mv.z);
@@ -863,18 +674,16 @@ const nodeMat = new THREE.ShaderMaterial({
863
674
  fragmentShader: \`
864
675
  varying vec3 vColor;
865
676
  varying float vAlpha;
866
- varying float vActivity;
867
677
  void main(){
868
678
  vec2 c = gl_PointCoord - vec2(.5);
869
679
  float d = length(c);
870
680
  if(d > .5) discard;
871
- // Sharp saturated core with minimal glow — preserves level color
872
- float core = exp(-d*d*180.0) * 0.95;
873
- float halo = exp(-d*d*22.0) * 0.25;
874
- float outer = exp(-d*d*6.0) * 0.06;
875
- float intensity = core + halo + outer;
876
- // Keep color saturated minimal brightness boost on core
877
- gl_FragColor = vec4(vColor * (0.85 + core * 0.15), intensity * vAlpha);
681
+ float core = exp(-d*d*100.0);
682
+ float g1 = exp(-d*d*18.0) * .45;
683
+ float g2 = exp(-d*d*4.0) * .15;
684
+ float g3 = exp(-d*d*1.2) * .06;
685
+ float i = core + g1 + g2 + g3;
686
+ gl_FragColor = vec4(vColor * (1.0 + core * .5), i * vAlpha);
878
687
  }
879
688
  \`,
880
689
  transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
@@ -928,6 +737,7 @@ let pData = [];
928
737
  let nCount = 0;
929
738
  let eCount = 0;
930
739
  let pCount = 0;
740
+ let orbitRings = [];
931
741
 
932
742
  const PARTICLES_PER_EDGE = 3;
933
743
  const tc = new THREE.Color();
@@ -938,6 +748,8 @@ function rebuildScene() {
938
748
  if (nodeGeo) { nodeGeo.dispose(); scene.remove(nodePoints); }
939
749
  if (edgeGeo) { edgeGeo.dispose(); scene.remove(edgeLines); }
940
750
  if (particleGeo) { particleGeo.dispose(); scene.remove(particlePoints); }
751
+ orbitRings.forEach(r => { r.geometry.dispose(); r.material.dispose(); scene.remove(r); });
752
+ orbitRings = [];
941
753
 
942
754
  nodes = BRAIN_DATA.nodes;
943
755
  nCount = nodes.length;
@@ -950,17 +762,30 @@ function rebuildScene() {
950
762
  byLevel[lv].push(i);
951
763
  });
952
764
 
953
- // Build nodeIdxMap first (needed for force layout)
954
- nodeIdxMap = {};
955
- nodes.forEach((n, i) => { nodeIdxMap[n.id] = i; });
956
-
957
- // Force-directed 3D layout
958
- const forcePos = computeForceLayout(nodes, BRAIN_DATA.edges, nodeIdxMap);
765
+ // Compute radial spiral positions
959
766
  positions = new Array(nCount);
960
- for (let i = 0; i < nCount; i++) {
961
- const fp = forcePos[i] || { x: 0, y: 0, z: 0 };
962
- positions[i] = new THREE.Vector3(fp.x, fp.y, fp.z);
767
+ for (const [lv, indices] of Object.entries(byLevel)) {
768
+ const s = SPATIAL[lv] || SPATIAL[3];
769
+ const c = indices.length;
770
+ indices.forEach((ni, j) => {
771
+ const angle = (j / Math.max(c, 1)) * Math.PI * 2 + (srand(ni * 19) - 0.5) * 0.6;
772
+ const r = s.iR + srand(ni * 7) * (s.oR - s.iR);
773
+ const spiral = angle + r * 0.003 + srand(ni * 23) * 0.4;
774
+ const y = (s.yBase || 0) + (srand(ni * 11) - 0.5) * s.yS;
775
+ // Add organic jitter so it doesn't look like perfect rings
776
+ const jitterX = (srand(ni * 29) - 0.5) * r * 0.15;
777
+ const jitterZ = (srand(ni * 37) - 0.5) * r * 0.15;
778
+ positions[ni] = new THREE.Vector3(
779
+ Math.cos(spiral) * r + jitterX,
780
+ y,
781
+ Math.sin(spiral) * r + jitterZ
782
+ );
783
+ });
963
784
  }
785
+
786
+ // Build adjacency + nodeIdxMap
787
+ nodeIdxMap = {};
788
+ nodes.forEach((n, i) => { nodeIdxMap[n.id] = i; });
964
789
  adj = {};
965
790
  nodeEdgeMap = {};
966
791
  BRAIN_DATA.edges.forEach((e, ei) => {
@@ -976,33 +801,6 @@ function rebuildScene() {
976
801
  nodeEdgeMap[ti].push(ei);
977
802
  });
978
803
 
979
- // ---- NODE COLOR: blend level + dominant edge type ----
980
- // Pre-compute dominant edge type per node
981
- const nodeEdgeTypeCounts = {};
982
- for (const e of BRAIN_DATA.edges) {
983
- const si = nodeIdxMap[e.source], ti = nodeIdxMap[e.target];
984
- if (si === undefined || ti === undefined) continue;
985
- const type = e.type || 'related_to';
986
- if (!nodeEdgeTypeCounts[si]) nodeEdgeTypeCounts[si] = {};
987
- if (!nodeEdgeTypeCounts[ti]) nodeEdgeTypeCounts[ti] = {};
988
- nodeEdgeTypeCounts[si][type] = (nodeEdgeTypeCounts[si][type] || 0) + 1;
989
- nodeEdgeTypeCounts[ti][type] = (nodeEdgeTypeCounts[ti][type] || 0) + 1;
990
- }
991
- const nodeDomType = {};
992
- for (const idx in nodeEdgeTypeCounts) {
993
- let maxT = 'default', maxC = 0;
994
- for (const [t, c] of Object.entries(nodeEdgeTypeCounts[idx])) {
995
- if (c > maxC) { maxC = c; maxT = t; }
996
- }
997
- nodeDomType[idx] = maxT;
998
- }
999
- // Max degree for brightness scaling
1000
- let maxDegree = 1;
1001
- for (let i = 0; i < nCount; i++) {
1002
- const deg = adj[i] ? adj[i].size : 0;
1003
- if (deg > maxDegree) maxDegree = deg;
1004
- }
1005
-
1006
804
  // ---- NODE POINTS ----
1007
805
  nodeGeo = new THREE.BufferGeometry();
1008
806
  const nPos = new Float32Array(nCount * 3);
@@ -1010,49 +808,60 @@ function rebuildScene() {
1010
808
  const nSize = new Float32Array(nCount);
1011
809
  const nHighlight = new Float32Array(nCount);
1012
810
  const nPulse = new Float32Array(nCount);
1013
- const nActivity = new Float32Array(nCount);
1014
- const ec = new THREE.Color();
1015
811
 
1016
812
  for (let i = 0; i < nCount; i++) {
1017
813
  const p = positions[i];
1018
814
  const n = nodes[i];
1019
- const s = LEVEL_STYLE[n.level] || LEVEL_STYLE[3];
815
+ const s = SPATIAL[n.level] || SPATIAL[3];
1020
816
  nPos[i * 3] = p.x; nPos[i * 3 + 1] = p.y; nPos[i * 3 + 2] = p.z;
1021
- // Base level color
1022
817
  tc.set(LEVEL_COLORS_HEX[n.level] || 0x00FFFF);
1023
- // Blend with dominant edge type color (35% influence) for color variety
1024
- const domType = nodeDomType[i] || 'default';
1025
- ec.set(EDGE_COLORS[domType] || EDGE_COLORS.default);
1026
- tc.lerp(ec, 0.35);
1027
- // Brightness by degree: low-degree dimmer, high-degree brighter
1028
- const deg = adj[i] ? adj[i].size : 0;
1029
- const bright = 0.6 + (deg / maxDegree) * 0.4;
1030
- tc.multiplyScalar(bright);
1031
818
  nCol[i * 3] = tc.r; nCol[i * 3 + 1] = tc.g; nCol[i * 3 + 2] = tc.b;
1032
819
  nSize[i] = s.size;
1033
820
  nHighlight[i] = 1.0;
1034
821
  nPulse[i] = s.pulse;
1035
- nActivity[i] = s.activity || 0.5;
1036
822
  }
1037
823
  nodeGeo.setAttribute('position', new THREE.BufferAttribute(nPos, 3));
1038
824
  nodeGeo.setAttribute('aColor', new THREE.BufferAttribute(nCol, 3));
1039
825
  nodeGeo.setAttribute('aSize', new THREE.BufferAttribute(nSize, 1));
1040
826
  nodeGeo.setAttribute('aHighlight', new THREE.BufferAttribute(nHighlight, 1));
1041
827
  nodeGeo.setAttribute('aPulse', new THREE.BufferAttribute(nPulse, 1));
1042
- nodeGeo.setAttribute('aActivity', new THREE.BufferAttribute(nActivity, 1));
1043
828
  nodePoints = new THREE.Points(nodeGeo, nodeMat);
1044
829
  scene.add(nodePoints);
1045
830
 
831
+ // ---- ORBIT RINGS (one per layer) ----
832
+ for (let lv = 1; lv <= 6; lv++) {
833
+ const s = SPATIAL[lv];
834
+ if (!s || !(byLevel[lv] || []).length) continue;
835
+ const ringRadius = s.oR * 0.85;
836
+ const ringGeo = new THREE.RingGeometry(ringRadius - 0.3, ringRadius + 0.3, 96);
837
+ const ringColor = new THREE.Color(LEVEL_COLORS_HEX[lv] || 0x00FFFF);
838
+ const ringMat = new THREE.MeshBasicMaterial({
839
+ color: ringColor, transparent: true, opacity: 0.08, side: THREE.DoubleSide,
840
+ blending: THREE.AdditiveBlending, depthWrite: false,
841
+ });
842
+ const ring = new THREE.Mesh(ringGeo, ringMat);
843
+ ring.rotation.x = -Math.PI / 2;
844
+ ring.position.y = s.yBase;
845
+ ring.userData.level = lv;
846
+ scene.add(ring);
847
+ orbitRings.push(ring);
848
+ }
849
+
1046
850
  // ---- EDGES (LineSegments) ----
851
+ // Prioritize cross-level edges (vertical) over intra-level (horizontal)
1047
852
  const allEdges = [];
1048
853
  BRAIN_DATA.edges.forEach((e, i) => {
1049
854
  const si = nodeIdxMap[e.source], ti = nodeIdxMap[e.target];
1050
855
  if (si !== undefined && ti !== undefined) {
1051
- allEdges.push({ si, ti, weight: e.weight, type: e.type, idx: i });
856
+ const crossLevel = nodes[si].level !== nodes[ti].level;
857
+ allEdges.push({ si, ti, weight: e.weight, type: e.type, idx: i, crossLevel });
1052
858
  }
1053
859
  });
1054
- // Sort by weight (strongest connections first)
1055
- allEdges.sort((a, b) => b.weight - a.weight);
860
+ // Sort: cross-level edges first (they create the beautiful vertical connections)
861
+ allEdges.sort((a, b) => {
862
+ if (a.crossLevel !== b.crossLevel) return b.crossLevel ? 1 : -1;
863
+ return b.weight - a.weight; // then by weight
864
+ });
1056
865
  validEdges = allEdges.slice(0, MAX_RENDERED_EDGES);
1057
866
 
1058
867
  eCount = validEdges.length;
@@ -1065,18 +874,17 @@ function rebuildScene() {
1065
874
  const alphaScale = Math.min(1.0, 3000 / eCount);
1066
875
 
1067
876
  for (let i = 0; i < eCount; i++) {
1068
- const { si, ti, weight, type } = validEdges[i];
877
+ const { si, ti, weight, crossLevel } = validEdges[i];
1069
878
  const s = positions[si], t = positions[ti];
1070
879
  const o = i * 6;
1071
880
  ePos[o] = s.x; ePos[o + 1] = s.y; ePos[o + 2] = s.z;
1072
881
  ePos[o + 3] = t.x; ePos[o + 4] = t.y; ePos[o + 5] = t.z;
1073
- // Color by edge type for galaxy look
1074
- const edgeColor = EDGE_COLORS[type] || EDGE_COLORS.default;
1075
- sc.set(edgeColor); dc.set(edgeColor);
882
+ sc.set(LEVEL_COLORS_HEX[nodes[si].level] || 0x00FFFF);
883
+ dc.set(LEVEL_COLORS_HEX[nodes[ti].level] || 0x00FFFF);
1076
884
  eCol[o] = sc.r; eCol[o + 1] = sc.g; eCol[o + 2] = sc.b;
1077
885
  eCol[o + 3] = dc.r; eCol[o + 4] = dc.g; eCol[o + 5] = dc.b;
1078
- // Boosted edge alpha for colorful connections
1079
- const baseAlpha = 0.12 + weight * 0.28;
886
+ // Cross-level edges brighter, intra-level edges dimmer
887
+ const baseAlpha = crossLevel ? (0.08 + weight * 0.12) : (0.03 + weight * 0.04);
1080
888
  eAlpha[i * 2] = baseAlpha * alphaScale;
1081
889
  eAlpha[i * 2 + 1] = baseAlpha * alphaScale;
1082
890
  }
@@ -1088,13 +896,13 @@ function rebuildScene() {
1088
896
  edgeLines = new THREE.LineSegments(edgeGeo, edgeMat);
1089
897
  scene.add(edgeLines);
1090
898
 
1091
- // ---- FLOWING PARTICLES (on stronger connections) ----
899
+ // ---- FLOWING PARTICLES (only on cross-level edges for clarity) ----
1092
900
  pData = [];
1093
901
  const maxParticleEdges = Math.min(eCount, 2000);
1094
902
  for (let i = 0; i < maxParticleEdges; i++) {
1095
- const { si, ti, weight } = validEdges[i];
1096
- if (weight < 0.4) continue; // only on stronger connections
1097
- const particleCount = weight > 0.7 ? 2 : 1;
903
+ const { si, ti, weight, crossLevel } = validEdges[i];
904
+ if (!crossLevel) continue; // only particles on vertical connections
905
+ const particleCount = weight > 0.6 ? 2 : 1;
1098
906
  for (let j = 0; j < particleCount; j++) {
1099
907
  pData.push({
1100
908
  edgeIdx: i, progress: srand(i * 100 + j * 31),
@@ -1244,8 +1052,8 @@ function updateHighlights() {
1244
1052
  const activeEdgeTypes = getActiveEdgeTypes();
1245
1053
  const alphaS = Math.min(1.0, 3000 / eCount);
1246
1054
  for (let i = 0; i < eCount; i++) {
1247
- const { si, ti, weight, type } = validEdges[i];
1248
- const baseA = 0.06 + weight * 0.14;
1055
+ const { si, ti, weight, type, crossLevel } = validEdges[i];
1056
+ const baseA = crossLevel ? (0.08 + weight * 0.12) : (0.03 + weight * 0.04);
1249
1057
  let a = baseA * alphaS;
1250
1058
 
1251
1059
  // Level toggle: hide edges connected to hidden levels
@@ -1273,6 +1081,11 @@ function updateHighlights() {
1273
1081
  }
1274
1082
  ea.needsUpdate = true;
1275
1083
 
1084
+ // Update orbit ring visibility
1085
+ orbitRings.forEach(ring => {
1086
+ const lv = ring.userData.level;
1087
+ ring.material.opacity = levelToggles[lv] === false ? 0 : 0.08;
1088
+ });
1276
1089
  }
1277
1090
 
1278
1091
  function getActiveEdgeTypes() {
@@ -1335,7 +1148,7 @@ function closeSidebar(evt) {
1335
1148
  controls.autoRotate = true;
1336
1149
  camTween = {
1337
1150
  startPos: camera.position.clone(), startLookAt: controls.target.clone(),
1338
- targetPos: new THREE.Vector3(600, 350, 600), targetLookAt: new THREE.Vector3(0, 0, 0),
1151
+ targetPos: new THREE.Vector3(500, 350, 700), targetLookAt: new THREE.Vector3(0, 60, 0),
1339
1152
  progress: 0
1340
1153
  };
1341
1154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/cli/brain/template.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEtC,OAAO;;;;;;0CAMiC,IAAI,CAAC,IAAI,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uCA2ZxB,IAAI,CAAC,IAAI,CAAC,UAAU,iBAAiB,IAAI,CAAC,IAAI,CAAC,UAAU,eAAe,IAAI,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW;;wCAE3L,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;uCACpD,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAkKvE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAoiCL,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAukBvE,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/cli/brain/template.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEtC,OAAO;;;;;;0CAMiC,IAAI,CAAC,IAAI,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uCA2ZxB,IAAI,CAAC,IAAI,CAAC,UAAU,iBAAiB,IAAI,CAAC,IAAI,CAAC,UAAU,eAAe,IAAI,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW;;wCAE3L,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;uCACpD,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAkKvE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAy2BL,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAukBvE,CAAC;AACT,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/chat.ts"],"names":[],"mappings":"AA0DA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAiMD,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA28DrE"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/chat.ts"],"names":[],"mappings":"AA0DA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAiMD,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA4lErE"}