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,
|
|
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
|
-
//
|
|
597
|
-
//
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
3: {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
6: {
|
|
596
|
+
// Organic nebula layout — layers 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 =
|
|
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, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
|
@@ -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.
|
|
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(
|
|
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.
|
|
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 =
|
|
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) *
|
|
820
|
-
starPos[i * 3 + 1] = (srand(i * 37) - 0.5) *
|
|
821
|
-
starPos[i * 3 + 2] = (srand(i * 41) - 0.5) *
|
|
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.
|
|
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
|
-
|
|
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 *
|
|
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
|
-
|
|
872
|
-
float
|
|
873
|
-
float
|
|
874
|
-
float
|
|
875
|
-
float
|
|
876
|
-
|
|
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
|
-
//
|
|
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 (
|
|
961
|
-
const
|
|
962
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
1055
|
-
allEdges.sort((a, b) =>
|
|
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,
|
|
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
|
-
|
|
1074
|
-
|
|
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
|
-
//
|
|
1079
|
-
const baseAlpha = 0.12 + weight * 0.
|
|
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
|
|
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 (
|
|
1097
|
-
const particleCount = weight > 0.
|
|
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.
|
|
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(
|
|
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
|
|
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,
|
|
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"}
|