@timmeck/brain-core 2.34.0 → 2.35.0

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.
@@ -177,8 +177,8 @@ body::before{
177
177
  .notif-msg{flex:1;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
178
178
 
179
179
  /* ── Neural Canvas ─────────────────────────────────── */
180
- .neural-wrap{position:relative;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border)}
181
- .neural-canvas{display:block;width:100%;background:transparent;cursor:grab}
180
+ .neural-wrap{position:relative;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border);min-height:280px}
181
+ .neural-canvas{display:block;width:100%;height:100%;background:transparent;cursor:grab}
182
182
  .neural-canvas:active{cursor:grabbing}
183
183
  .neural-controls{
184
184
  position:absolute;bottom:10px;right:10px;display:flex;gap:4px;
@@ -346,9 +346,9 @@ body::before{
346
346
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
347
347
  <span>Overview</span>
348
348
  </div>
349
- <div class="nav-item" data-page="neural">
350
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/><line x1="7" y1="7" x2="10" y2="10"/><line x1="14" y1="10" x2="17" y2="7"/><line x1="7" y1="17" x2="10" y2="14"/><line x1="14" y1="14" x2="17" y2="17"/></svg>
351
- <span>Neural</span>
349
+ <div class="nav-item" data-page="consciousness">
350
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="8" stroke-dasharray="4 3"/><circle cx="12" cy="12" r="11" stroke-dasharray="2 4" opacity="0.5"/><circle cx="11" cy="11" r="1.5" fill="currentColor" stroke="none"/></svg>
351
+ <span>Consciousness</span>
352
352
  </div>
353
353
  <div class="nav-item" data-page="thoughts">
354
354
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
@@ -395,32 +395,29 @@ body::before{
395
395
  <div class="page active" id="page-overview">
396
396
  <div class="stat-grid" id="overviewStats"></div>
397
397
  <div class="brain-grid" id="brainCards"></div>
398
- <div class="two-col">
399
- <div>
400
- <div class="section-title">Live Notifications</div>
401
- <div class="glass"><div class="notif-feed" id="notifFeed"></div></div>
402
- </div>
403
- <div>
404
- <div class="section-title">Neural Preview</div>
405
- <div class="neural-wrap glass" style="padding:0">
406
- <canvas class="neural-canvas" id="overviewCanvas" height="280"></canvas>
407
- <div class="neural-info" id="overviewInfo">0 nodes, 0 edges</div>
408
- </div>
398
+ <div style="margin-bottom:16px">
399
+ <div class="section-title">Consciousness</div>
400
+ <div class="neural-wrap glass" style="padding:0;background:rgba(5,8,16,0.95);height:300px">
401
+ <canvas class="neural-canvas" id="entityPreviewCanvas" width="800" height="300"></canvas>
402
+ <div class="neural-info" id="entityMoodLabel">--</div>
409
403
  </div>
410
404
  </div>
405
+ <div>
406
+ <div class="section-title">Live Notifications</div>
407
+ <div class="glass"><div class="notif-feed" id="notifFeed"></div></div>
408
+ </div>
411
409
  </div>
412
410
 
413
- <!-- NEURAL PAGE -->
414
- <div class="page" id="page-neural">
415
- <div class="section-title">Neural Network</div>
416
- <div class="neural-wrap" style="height:calc(100vh - 130px)">
417
- <canvas class="neural-canvas" id="neuralCanvas"></canvas>
418
- <div class="neural-info" id="neuralInfo">0 nodes, 0 edges</div>
419
- <div class="neural-controls">
420
- <button class="neural-btn" onclick="neuralZoom(1.2)" title="Zoom In">+</button>
421
- <button class="neural-btn" onclick="neuralZoom(0.8)" title="Zoom Out">-</button>
422
- <button class="neural-btn" onclick="neuralReset()" title="Reset">R</button>
411
+ <!-- CONSCIOUSNESS PAGE -->
412
+ <div class="page" id="page-consciousness">
413
+ <div class="neural-wrap" style="height:calc(100vh - 130px);background:rgba(5,8,16,0.95);border:1px solid var(--border)">
414
+ <canvas class="neural-canvas" id="entityFullCanvas"></canvas>
415
+ <div id="entityHud" style="position:absolute;top:16px;left:16px;pointer-events:none">
416
+ <div style="font-size:10px;text-transform:uppercase;letter-spacing:2px;color:var(--text-muted);margin-bottom:4px">Consciousness</div>
417
+ <div id="entityMoodFull" style="font-size:14px;font-weight:600;letter-spacing:1px;color:var(--cyan)">--</div>
418
+ <div id="entityScoreFull" style="font-size:10px;color:var(--text-dim);margin-top:2px">--</div>
423
419
  </div>
420
+ <div id="entityDimensions" style="position:absolute;bottom:16px;left:16px;right:16px;display:flex;gap:8px;flex-wrap:wrap;pointer-events:none"></div>
424
421
  </div>
425
422
  </div>
426
423
 
@@ -479,6 +476,7 @@ let state = {
479
476
  thoughts: [], engines: {}, stats: {},
480
477
  notifications: [], network: { nodes: [], edges: [] },
481
478
  codegen: null, selfmod: null,
479
+ emotional: { mood: 'reflective', score: 0.5, valence: 0, arousal: 0, dimensions: { frustration: 0, curiosity: 0.5, surprise: 0, confidence: 0.5, satisfaction: 0.5, stress: 0, momentum: 0, creativity: 0.5 } },
482
480
  };
483
481
  let connected = false;
484
482
  const startTime = Date.now();
@@ -518,6 +516,7 @@ function connect() {
518
516
  state.notifications.unshift(t);
519
517
  if (state.notifications.length > MAX_NOTIFS) state.notifications.pop();
520
518
  }
519
+ if (t.significance === 'breakthrough' || t.significance === 'notable') addFloatingThought(t);
521
520
  renderThoughts();
522
521
  renderNotifications();
523
522
  });
@@ -531,7 +530,6 @@ function connect() {
531
530
  });
532
531
  evtSource.addEventListener('network', function(e) {
533
532
  state.network = JSON.parse(e.data);
534
- renderNetwork();
535
533
  });
536
534
  evtSource.addEventListener('codegen:generated', function() { fetchCodegen(); });
537
535
  evtSource.addEventListener('codegen:approved', function() { fetchCodegen(); });
@@ -564,8 +562,8 @@ async function fetchInitial() {
564
562
  try {
565
563
  const r = await fetch('/api/network');
566
564
  state.network = await r.json();
567
- renderNetwork();
568
565
  } catch(e) { console.warn('fetchNetwork failed', e); }
566
+ fetchEmotional();
569
567
  fetchCodegen();
570
568
  fetchSelfmod();
571
569
  }
@@ -586,6 +584,21 @@ async function fetchSelfmod() {
586
584
  } catch(e) { console.warn('fetchSelfmod failed', e); }
587
585
  }
588
586
 
587
+ async function fetchEmotional() {
588
+ try {
589
+ const r = await fetch('/api/emotional');
590
+ const d = await r.json();
591
+ if (d && d.mood) {
592
+ // Smooth transition to new state
593
+ var old = state.emotional;
594
+ state.emotional = d;
595
+ state.emotional._prevMood = old.mood;
596
+ state.emotional._transitionStart = Date.now();
597
+ }
598
+ } catch(e) { /* emotional endpoint may not exist */ }
599
+ }
600
+ setInterval(fetchEmotional, 5000);
601
+
589
602
  // ═══ NAVIGATION ═══
590
603
  document.querySelectorAll('.nav-item').forEach(function(el) {
591
604
  el.addEventListener('click', function() {
@@ -594,7 +607,7 @@ document.querySelectorAll('.nav-item').forEach(function(el) {
594
607
  el.classList.add('active');
595
608
  var page = el.dataset.page;
596
609
  document.getElementById('page-' + page).classList.add('active');
597
- if (page === 'neural') requestAnimationFrame(function() { resizeNeuralCanvas(); });
610
+ if (page === 'consciousness') requestAnimationFrame(function() { resizeEntityCanvas(); });
598
611
  if (page === 'codegen') fetchCodegen();
599
612
  if (page === 'selfmod') fetchSelfmod();
600
613
  });
@@ -962,276 +975,395 @@ function renderIntelligence() {
962
975
  }
963
976
  }
964
977
 
965
- // ═══ NEURAL GRAPH ═══
966
- var nodeTypeColors = {
967
- project:'#00e5ff', error:'#ff3d3d', solution:'#00ff88',
968
- code_module:'#448aff', insight:'#ffd700', memory:'#b388ff'
969
- };
978
+ // ═══ THE ENTITY — CONSCIOUSNESS VISUALIZATION ═══
970
979
 
971
- var neuralNodes = [];
972
- var neuralEdges = [];
973
- var neuralZoomLevel = 1;
974
- var neuralPanX = 0, neuralPanY = 0;
975
- var neuralDragging = null;
976
- var neuralPanning = false;
977
- var neuralPanStart = { x: 0, y: 0 };
978
- var neuralHover = null;
979
- var neuralAnimId = null;
980
-
981
- function initNeuralGraph(canvasId) {
982
- var canvas = document.getElementById(canvasId);
983
- if (!canvas) return;
984
-
985
- canvas.addEventListener('mousedown', function(e) {
986
- var pos = getCanvasPos(canvas, e);
987
- var node = findNodeAt(pos.x, pos.y);
988
- if (node) {
989
- neuralDragging = node;
990
- node.pinned = true;
991
- } else {
992
- neuralPanning = true;
993
- neuralPanStart = { x: e.clientX - neuralPanX, y: e.clientY - neuralPanY };
994
- }
995
- });
980
+ var MOOD_COLORS = {
981
+ flow: { r:0, g:229, b:255, name:'Flow' },
982
+ excited: { r:255, g:215, b:0, name:'Excited' },
983
+ anxious: { r:255, g:61, b:61, name:'Anxious' },
984
+ reflective: { r:179, g:136, b:255, name:'Reflective' },
985
+ bored: { r:110, g:122, b:138, name:'Bored' },
986
+ determined: { r:224, g:230, b:240, name:'Determined' },
987
+ };
996
988
 
997
- canvas.addEventListener('mousemove', function(e) {
998
- var pos = getCanvasPos(canvas, e);
999
- if (neuralDragging) {
1000
- neuralDragging.x = pos.x;
1001
- neuralDragging.y = pos.y;
1002
- } else if (neuralPanning) {
1003
- neuralPanX = e.clientX - neuralPanStart.x;
1004
- neuralPanY = e.clientY - neuralPanStart.y;
1005
- } else {
1006
- neuralHover = findNodeAt(pos.x, pos.y);
1007
- canvas.style.cursor = neuralHover ? 'pointer' : 'grab';
1008
- }
989
+ // Current rendered color (smoothly interpolated)
990
+ var entityColor = { r:179, g:136, b:255 };
991
+ var entityTargetColor = { r:179, g:136, b:255 };
992
+
993
+ // Floating thoughts around entity
994
+ var floatingThoughts = [];
995
+ var MAX_FLOATING = 12;
996
+
997
+ // Ambient particles
998
+ var entityParticles = [];
999
+ var PARTICLE_COUNT = 80;
1000
+
1001
+ // Tentacle state
1002
+ var tentacles = [];
1003
+ var TENTACLE_COUNT = 10;
1004
+
1005
+ // Initialize tentacles with random phases
1006
+ for (var ti = 0; ti < TENTACLE_COUNT; ti++) {
1007
+ tentacles.push({
1008
+ angle: (ti / TENTACLE_COUNT) * Math.PI * 2,
1009
+ phase: Math.random() * Math.PI * 2,
1010
+ speed: 0.3 + Math.random() * 0.4,
1011
+ lengthMul: 0.8 + Math.random() * 0.4,
1012
+ widthMul: 0.7 + Math.random() * 0.6,
1009
1013
  });
1014
+ }
1010
1015
 
1011
- canvas.addEventListener('mouseup', function() {
1012
- if (neuralDragging) neuralDragging.pinned = false;
1013
- neuralDragging = null;
1014
- neuralPanning = false;
1016
+ // Initialize particles
1017
+ for (var pi = 0; pi < PARTICLE_COUNT; pi++) {
1018
+ entityParticles.push({
1019
+ angle: Math.random() * Math.PI * 2,
1020
+ dist: 0.5 + Math.random() * 2.5,
1021
+ speed: 0.1 + Math.random() * 0.3,
1022
+ size: 0.5 + Math.random() * 1.5,
1023
+ phase: Math.random() * Math.PI * 2,
1024
+ brightness: 0.2 + Math.random() * 0.5,
1015
1025
  });
1026
+ }
1016
1027
 
1017
- canvas.addEventListener('mouseleave', function() {
1018
- neuralDragging = null;
1019
- neuralPanning = false;
1020
- neuralHover = null;
1028
+ // Add floating thought from incoming SSE thought
1029
+ function addFloatingThought(thought) {
1030
+ if (floatingThoughts.length >= MAX_FLOATING) floatingThoughts.shift();
1031
+ var angle = Math.random() * Math.PI * 2;
1032
+ var col = engineColor(thought.engine);
1033
+ floatingThoughts.push({
1034
+ text: (thought.content || '').substring(0, 40),
1035
+ angle: angle,
1036
+ dist: 0.2,
1037
+ speed: 0.15 + Math.random() * 0.1,
1038
+ alpha: 1,
1039
+ color: col,
1040
+ born: Date.now(),
1041
+ life: 6000 + Math.random() * 4000,
1021
1042
  });
1022
-
1023
- canvas.addEventListener('wheel', function(e) {
1024
- e.preventDefault();
1025
- var factor = e.deltaY < 0 ? 1.1 : 0.9;
1026
- neuralZoomLevel = Math.max(0.2, Math.min(5, neuralZoomLevel * factor));
1027
- }, { passive: false });
1028
1043
  }
1029
1044
 
1030
- function getCanvasPos(canvas, e) {
1031
- var rect = canvas.getBoundingClientRect();
1045
+ function lerpColor(a, b, t) {
1032
1046
  return {
1033
- x: (e.clientX - rect.left - neuralPanX) / neuralZoomLevel,
1034
- y: (e.clientY - rect.top - neuralPanY) / neuralZoomLevel,
1047
+ r: a.r + (b.r - a.r) * t,
1048
+ g: a.g + (b.g - a.g) * t,
1049
+ b: a.b + (b.b - a.b) * t,
1035
1050
  };
1036
1051
  }
1037
1052
 
1038
- function findNodeAt(x, y) {
1039
- for (var i = 0; i < neuralNodes.length; i++) {
1040
- var n = neuralNodes[i];
1041
- var dx = n.x - x, dy = n.y - y;
1042
- if (dx * dx + dy * dy < (n.radius || 5) * (n.radius || 5) * 4) return n;
1043
- }
1044
- return null;
1045
- }
1046
-
1047
- function neuralZoom(factor) {
1048
- neuralZoomLevel = Math.max(0.2, Math.min(5, neuralZoomLevel * factor));
1053
+ function rgba(c, a) {
1054
+ return 'rgba(' + Math.round(c.r) + ',' + Math.round(c.g) + ',' + Math.round(c.b) + ',' + a + ')';
1049
1055
  }
1050
1056
 
1051
- function neuralReset() {
1052
- neuralZoomLevel = 1;
1053
- neuralPanX = 0;
1054
- neuralPanY = 0;
1055
- }
1056
-
1057
- function updateNeuralNodes(network) {
1058
- var existingMap = {};
1059
- for (var i = 0; i < neuralNodes.length; i++) existingMap[neuralNodes[i].id] = neuralNodes[i];
1060
- var newNodes = [];
1061
- var nodes = network.nodes || [];
1062
-
1063
- for (var j = 0; j < nodes.length; j++) {
1064
- var n = nodes[j];
1065
- var existing = existingMap[n.id];
1066
- if (existing) {
1067
- existing.label = n.label;
1068
- existing.type = n.type;
1069
- existing.importance = n.importance != null ? n.importance : 0.5;
1070
- newNodes.push(existing);
1071
- } else {
1072
- var angle = (j / Math.max(1, nodes.length)) * Math.PI * 2;
1073
- var r = 150 + Math.random() * 100;
1074
- newNodes.push({
1075
- id: n.id, label: n.label, type: n.type,
1076
- importance: n.importance != null ? n.importance : 0.5,
1077
- x: 300 + Math.cos(angle) * r + (Math.random() - 0.5) * 50,
1078
- y: 250 + Math.sin(angle) * r + (Math.random() - 0.5) * 50,
1079
- vx: 0, vy: 0,
1080
- radius: 3 + (n.importance != null ? n.importance : 0.5) * 5,
1081
- pinned: false,
1082
- });
1057
+ function resizeEntityCanvas() {
1058
+ var ids = ['entityFullCanvas', 'entityPreviewCanvas'];
1059
+ for (var i = 0; i < ids.length; i++) {
1060
+ var canvas = document.getElementById(ids[i]);
1061
+ if (!canvas) continue;
1062
+ var wrap = canvas.parentElement;
1063
+ var dpr = window.devicePixelRatio || 1;
1064
+ var w = wrap.clientWidth, h = wrap.clientHeight;
1065
+ if (w > 0 && h > 0) {
1066
+ canvas.width = w * dpr;
1067
+ canvas.height = h * dpr;
1068
+ canvas.style.width = w + 'px';
1069
+ canvas.style.height = h + 'px';
1070
+ canvas.getContext('2d').setTransform(dpr, 0, 0, dpr, 0, 0);
1083
1071
  }
1084
1072
  }
1085
-
1086
- neuralNodes = newNodes;
1087
- neuralEdges = (network.edges || []).map(function(e) {
1088
- return {
1089
- source: e.source || e.source_id,
1090
- target: e.target || e.target_id,
1091
- weight: e.weight != null ? e.weight : 0.5,
1092
- };
1093
- });
1094
1073
  }
1095
1074
 
1096
- function simulatePhysics() {
1097
- var k = 0.008;
1098
- var repulsion = 800;
1099
- var damping = 0.92;
1100
- var gravity = 0.01;
1101
- var cx = 300, cy = 250;
1102
-
1103
- for (var i = 0; i < neuralNodes.length; i++) {
1104
- for (var j = i + 1; j < neuralNodes.length; j++) {
1105
- var a = neuralNodes[i], b = neuralNodes[j];
1106
- var dx = a.x - b.x, dy = a.y - b.y;
1107
- var dist = Math.sqrt(dx * dx + dy * dy) || 1;
1108
- var f = repulsion / (dist * dist);
1109
- dx = (dx / dist) * f;
1110
- dy = (dy / dist) * f;
1111
- if (!a.pinned) { a.vx += dx; a.vy += dy; }
1112
- if (!b.pinned) { b.vx -= dx; b.vy -= dy; }
1113
- }
1114
- }
1115
-
1116
- var nodeMap = {};
1117
- for (var m = 0; m < neuralNodes.length; m++) nodeMap[neuralNodes[m].id] = neuralNodes[m];
1118
- for (var e = 0; e < neuralEdges.length; e++) {
1119
- var edge = neuralEdges[e];
1120
- var ea = nodeMap[edge.source], eb = nodeMap[edge.target];
1121
- if (!ea || !eb) continue;
1122
- var edx = eb.x - ea.x, edy = eb.y - ea.y;
1123
- var edist = Math.sqrt(edx * edx + edy * edy) || 1;
1124
- var ideal = 60;
1125
- var ef = k * (edist - ideal);
1126
- var efx = (edx / edist) * ef, efy = (edy / edist) * ef;
1127
- if (!ea.pinned) { ea.vx += efx; ea.vy += efy; }
1128
- if (!eb.pinned) { eb.vx -= efx; eb.vy -= efy; }
1129
- }
1075
+ function drawEntity(canvasId, compact) {
1076
+ var canvas = document.getElementById(canvasId);
1077
+ if (!canvas) return;
1130
1078
 
1131
- for (var n = 0; n < neuralNodes.length; n++) {
1132
- var node = neuralNodes[n];
1133
- if (node.pinned) continue;
1134
- node.vx += (cx - node.x) * gravity;
1135
- node.vy += (cy - node.y) * gravity;
1136
- node.vx *= damping;
1137
- node.vy *= damping;
1138
- node.x += node.vx;
1139
- node.y += node.vy;
1079
+ // Ensure canvas is sized to its container
1080
+ var wrap = canvas.parentElement;
1081
+ var dpr = window.devicePixelRatio || 1;
1082
+ var ww = wrap ? wrap.clientWidth : 0;
1083
+ var wh = wrap ? wrap.clientHeight : 0;
1084
+ if (ww < 20 || wh < 20) return;
1085
+
1086
+ // Auto-resize if needed
1087
+ if (Math.abs(canvas.width - ww * dpr) > 2 || Math.abs(canvas.height - wh * dpr) > 2) {
1088
+ canvas.width = ww * dpr;
1089
+ canvas.height = wh * dpr;
1090
+ canvas.style.width = ww + 'px';
1091
+ canvas.style.height = wh + 'px';
1140
1092
  }
1141
- }
1142
1093
 
1143
- function drawNeuralCanvas(canvasId, isOverview) {
1144
- var canvas = document.getElementById(canvasId);
1145
- if (!canvas) return;
1146
1094
  var ctx = canvas.getContext('2d');
1147
- var w = canvas.width, h = canvas.height;
1095
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1096
+ var w = ww, h = wh;
1148
1097
 
1098
+ var cx = w / 2, cy = h / 2;
1099
+ var time = Date.now() / 1000;
1100
+ var em = state.emotional || {};
1101
+ var dim = em.dimensions || {};
1102
+
1103
+ // Update target color from mood
1104
+ var mc = MOOD_COLORS[em.mood] || MOOD_COLORS.reflective;
1105
+ entityTargetColor = { r: mc.r, g: mc.g, b: mc.b };
1106
+ entityColor = lerpColor(entityColor, entityTargetColor, 0.03);
1107
+
1108
+ // Clear
1149
1109
  ctx.clearRect(0, 0, w, h);
1150
- ctx.save();
1151
- ctx.translate(neuralPanX, neuralPanY);
1152
- ctx.scale(neuralZoomLevel, neuralZoomLevel);
1153
-
1154
- var nodeMap = {};
1155
- for (var m = 0; m < neuralNodes.length; m++) nodeMap[neuralNodes[m].id] = neuralNodes[m];
1156
-
1157
- // Edges
1158
- for (var e = 0; e < neuralEdges.length; e++) {
1159
- var edge = neuralEdges[e];
1160
- var a = nodeMap[edge.source], b = nodeMap[edge.target];
1161
- if (!a || !b) continue;
1162
- ctx.beginPath();
1163
- ctx.moveTo(a.x, a.y);
1164
- ctx.lineTo(b.x, b.y);
1165
- ctx.strokeStyle = 'rgba(0,229,255,' + Math.min(0.3, edge.weight * 0.4) + ')';
1166
- ctx.lineWidth = 0.5 + edge.weight;
1167
- ctx.stroke();
1110
+
1111
+ // --- Breathing ---
1112
+ var breathSpeed = 0.4 + (dim.stress || 0) * 1.2;
1113
+ var breathAmp = 0.06 + (dim.curiosity || 0) * 0.04;
1114
+ var breath = 1 + Math.sin(time * breathSpeed) * breathAmp;
1115
+
1116
+ // Base radius — compact is bigger relative since canvas is smaller
1117
+ var minDim = Math.min(w, h);
1118
+ var baseR = compact ? minDim * 0.28 : minDim * 0.13;
1119
+ var activity = Math.min(1, (state.thoughts.length || 0) / 80);
1120
+ var entityR = baseR * breath * (0.85 + activity * 0.15);
1121
+
1122
+ // --- Background nebula (full only) ---
1123
+ if (!compact) {
1124
+ var nebula = ctx.createRadialGradient(cx, cy, entityR * 0.5, cx, cy, minDim * 0.45);
1125
+ nebula.addColorStop(0, rgba(entityColor, 0.04));
1126
+ nebula.addColorStop(0.4, rgba(entityColor, 0.015));
1127
+ nebula.addColorStop(1, 'rgba(5,8,16,0)');
1128
+ ctx.fillStyle = nebula;
1129
+ ctx.fillRect(0, 0, w, h);
1168
1130
  }
1169
1131
 
1170
- // Nodes
1171
- for (var i = 0; i < neuralNodes.length; i++) {
1172
- var n = neuralNodes[i];
1173
- var col = nodeTypeColors[n.type] || '#6e7a8a';
1174
- var r = n.radius || 4;
1175
- var isHovered = neuralHover && neuralHover.id === n.id;
1132
+ // --- Tentacles (full only) ---
1133
+ if (!compact) {
1134
+ var curiosity = dim.curiosity || 0.5;
1135
+ var creativity = dim.creativity || 0.5;
1136
+ var stress = dim.stress || 0;
1137
+ var tentacleLen = entityR * (1.5 + curiosity * 2.0 + creativity * 0.8);
1138
+ var tentacleAlpha = 0.12 + (1 - stress) * 0.15;
1139
+
1140
+ for (var t = 0; t < tentacles.length; t++) {
1141
+ var ten = tentacles[t];
1142
+ var baseAngle = ten.angle + Math.sin(time * ten.speed + ten.phase) * 0.3;
1143
+ var len = tentacleLen * ten.lengthMul;
1144
+ var wid = (3 + creativity * 4) * ten.widthMul;
1145
+
1146
+ // Start point on orb surface
1147
+ var sx = cx + Math.cos(baseAngle) * entityR * 0.9;
1148
+ var sy = cy + Math.sin(baseAngle) * entityR * 0.9;
1149
+
1150
+ // Control points with organic wave
1151
+ var wave1 = Math.sin(time * 0.7 + ten.phase * 2) * 0.4;
1152
+ var wave2 = Math.cos(time * 0.5 + ten.phase * 3) * 0.5;
1153
+ var cp1x = cx + Math.cos(baseAngle + wave1) * (entityR + len * 0.35);
1154
+ var cp1y = cy + Math.sin(baseAngle + wave1) * (entityR + len * 0.35);
1155
+ var cp2x = cx + Math.cos(baseAngle + wave2) * (entityR + len * 0.7);
1156
+ var cp2y = cy + Math.sin(baseAngle + wave2) * (entityR + len * 0.7);
1157
+ var ex = cx + Math.cos(baseAngle + wave1 * 0.5 + wave2 * 0.3) * (entityR + len);
1158
+ var ey = cy + Math.sin(baseAngle + wave1 * 0.5 + wave2 * 0.3) * (entityR + len);
1159
+
1160
+ // Draw tentacle with gradient
1161
+ ctx.beginPath();
1162
+ ctx.moveTo(sx, sy);
1163
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ex, ey);
1164
+ var grad = ctx.createLinearGradient(sx, sy, ex, ey);
1165
+ grad.addColorStop(0, rgba(entityColor, tentacleAlpha));
1166
+ grad.addColorStop(0.6, rgba(entityColor, tentacleAlpha * 0.4));
1167
+ grad.addColorStop(1, rgba(entityColor, 0));
1168
+ ctx.strokeStyle = grad;
1169
+ ctx.lineWidth = wid;
1170
+ ctx.lineCap = 'round';
1171
+ ctx.stroke();
1176
1172
 
1177
- if (isHovered || n.importance > 0.7) {
1173
+ // Thin inner glow line
1178
1174
  ctx.beginPath();
1179
- ctx.arc(n.x, n.y, r * 2.5, 0, Math.PI * 2);
1180
- ctx.fillStyle = col + '14';
1181
- ctx.fill();
1175
+ ctx.moveTo(sx, sy);
1176
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ex, ey);
1177
+ ctx.strokeStyle = rgba({ r: 255, g: 255, b: 255 }, tentacleAlpha * 0.3);
1178
+ ctx.lineWidth = 1;
1179
+ ctx.stroke();
1182
1180
  }
1181
+ }
1183
1182
 
1184
- ctx.beginPath();
1185
- ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
1186
- ctx.fillStyle = col;
1187
- ctx.fill();
1183
+ // --- Dimension Ring (full only) ---
1184
+ if (!compact) {
1185
+ var ringR = entityR * 2.8;
1186
+ var dimNames = ['frustration','curiosity','surprise','confidence','satisfaction','stress','momentum','creativity'];
1187
+ var dimColors = ['#ff3d3d','#40c4ff','#ffab40','#00ff88','#69f0ae','#ff6e40','#b2ff59','#ea80fc'];
1188
+ var arcLen = (Math.PI * 2) / dimNames.length;
1189
+ var gap = 0.08;
1188
1190
 
1189
- if (isHovered) {
1190
- ctx.strokeStyle = '#fff';
1191
- ctx.lineWidth = 1.5;
1191
+ for (var di = 0; di < dimNames.length; di++) {
1192
+ var dimVal = dim[dimNames[di]] || 0;
1193
+ var startA = di * arcLen - Math.PI / 2 + gap / 2;
1194
+ var endA = startA + arcLen - gap;
1195
+ var alpha = 0.08 + dimVal * 0.25;
1196
+
1197
+ ctx.beginPath();
1198
+ ctx.arc(cx, cy, ringR, startA, endA);
1199
+ ctx.strokeStyle = dimColors[di] + Math.round(alpha * 255).toString(16).padStart(2, '0');
1200
+ ctx.lineWidth = 2 + dimVal * 4;
1201
+ ctx.lineCap = 'round';
1192
1202
  ctx.stroke();
1193
- }
1194
1203
 
1195
- if (isHovered || (!isOverview && neuralZoomLevel > 0.8 && n.importance > 0.6)) {
1196
- ctx.fillStyle = 'rgba(224,230,240,0.8)';
1197
- ctx.font = '9px ' + getComputedStyle(document.body).fontFamily;
1198
- ctx.fillText((n.label || '').substring(0, 30), n.x + r + 3, n.y + 3);
1204
+ // Dimension label
1205
+ if (dimVal > 0.3) {
1206
+ var labelAngle = (startA + endA) / 2;
1207
+ var labelR = ringR + 14;
1208
+ var lx = cx + Math.cos(labelAngle) * labelR;
1209
+ var ly = cy + Math.sin(labelAngle) * labelR;
1210
+ ctx.fillStyle = dimColors[di] + '80';
1211
+ ctx.font = '8px ' + getComputedStyle(document.body).fontFamily;
1212
+ ctx.textAlign = 'center';
1213
+ ctx.textBaseline = 'middle';
1214
+ ctx.fillText(dimNames[di].substring(0, 4).toUpperCase(), lx, ly);
1215
+ }
1199
1216
  }
1200
1217
  }
1201
1218
 
1202
- ctx.restore();
1203
- }
1219
+ // --- Particles ---
1220
+ var particleDensity = compact ? 0.3 : 1;
1221
+ var momentum = dim.momentum || 0;
1222
+ for (var p = 0; p < entityParticles.length; p++) {
1223
+ if (p > entityParticles.length * particleDensity) break;
1224
+ var part = entityParticles[p];
1225
+ part.angle += (part.speed * (0.5 + momentum)) * 0.016;
1226
+ var pdist = entityR * part.dist + Math.sin(time * 0.5 + part.phase) * entityR * 0.2;
1227
+ var px = cx + Math.cos(part.angle) * pdist;
1228
+ var py = cy + Math.sin(part.angle) * pdist;
1229
+ var pAlpha = part.brightness * (0.3 + Math.sin(time * 0.8 + part.phase) * 0.2);
1204
1230
 
1205
- function resizeNeuralCanvas() {
1206
- var canvas = document.getElementById('neuralCanvas');
1207
- if (!canvas) return;
1208
- var wrap = canvas.parentElement;
1209
- canvas.width = wrap.clientWidth;
1210
- canvas.height = wrap.clientHeight;
1211
- }
1231
+ ctx.beginPath();
1232
+ ctx.arc(px, py, part.size, 0, Math.PI * 2);
1233
+ ctx.fillStyle = rgba(entityColor, pAlpha);
1234
+ ctx.fill();
1235
+ }
1212
1236
 
1213
- function renderNetwork() {
1214
- updateNeuralNodes(state.network);
1215
- var txt = neuralNodes.length + ' nodes, ' + neuralEdges.length + ' edges';
1216
- var info1 = document.getElementById('overviewInfo');
1217
- var info2 = document.getElementById('neuralInfo');
1218
- if (info1) info1.textContent = txt;
1219
- if (info2) info2.textContent = txt;
1220
- }
1237
+ // --- Outer Glow ---
1238
+ var glowR = entityR * (compact ? 2.5 : 3.5);
1239
+ var outerGlow = ctx.createRadialGradient(cx, cy, entityR * 0.5, cx, cy, glowR);
1240
+ outerGlow.addColorStop(0, rgba(entityColor, 0.35));
1241
+ outerGlow.addColorStop(0.2, rgba(entityColor, 0.15));
1242
+ outerGlow.addColorStop(0.5, rgba(entityColor, 0.05));
1243
+ outerGlow.addColorStop(1, rgba(entityColor, 0));
1244
+ ctx.beginPath();
1245
+ ctx.arc(cx, cy, glowR, 0, Math.PI * 2);
1246
+ ctx.fillStyle = outerGlow;
1247
+ ctx.fill();
1248
+
1249
+ // --- Main Orb ---
1250
+ // Layered gradients for depth
1251
+ var orbGrad = ctx.createRadialGradient(
1252
+ cx - entityR * 0.25, cy - entityR * 0.25, entityR * 0.05,
1253
+ cx, cy, entityR
1254
+ );
1255
+ orbGrad.addColorStop(0, 'rgba(255,255,255,0.95)');
1256
+ orbGrad.addColorStop(0.1, rgba({ r: Math.min(255, entityColor.r + 100), g: Math.min(255, entityColor.g + 100), b: Math.min(255, entityColor.b + 100) }, 0.9));
1257
+ orbGrad.addColorStop(0.4, rgba(entityColor, 0.85));
1258
+ orbGrad.addColorStop(0.7, rgba(entityColor, 0.6));
1259
+ orbGrad.addColorStop(1, rgba(entityColor, 0.2));
1260
+
1261
+ ctx.beginPath();
1262
+ ctx.arc(cx, cy, entityR, 0, Math.PI * 2);
1263
+ ctx.fillStyle = orbGrad;
1264
+ ctx.fill();
1265
+
1266
+ // Subtle edge ring
1267
+ ctx.beginPath();
1268
+ ctx.arc(cx, cy, entityR, 0, Math.PI * 2);
1269
+ ctx.strokeStyle = rgba(entityColor, 0.3);
1270
+ ctx.lineWidth = 1;
1271
+ ctx.stroke();
1272
+
1273
+ // --- Inner Eye / Nucleus ---
1274
+ // Shifts toward attention focus direction
1275
+ var att = state.attention;
1276
+ var eyeOffsetX = 0, eyeOffsetY = 0;
1277
+ if (att && att.topTopics && att.topTopics.length > 0) {
1278
+ // Use top topic index to determine direction
1279
+ var focusAngle = (att.topTopics.length > 1)
1280
+ ? (att.topTopics[0].score / (att.topTopics[0].score + att.topTopics[1].score)) * Math.PI * 2 + time * 0.05
1281
+ : time * 0.1;
1282
+ var focusStr = Math.min(1, (att.topTopics[0].score || 0) / 10);
1283
+ eyeOffsetX = Math.cos(focusAngle) * entityR * 0.15 * focusStr;
1284
+ eyeOffsetY = Math.sin(focusAngle) * entityR * 0.15 * focusStr;
1285
+ }
1286
+
1287
+ var eyeR = entityR * 0.2;
1288
+ var eyeGrad = ctx.createRadialGradient(
1289
+ cx + eyeOffsetX, cy + eyeOffsetY, 0,
1290
+ cx + eyeOffsetX, cy + eyeOffsetY, eyeR
1291
+ );
1292
+ eyeGrad.addColorStop(0, 'rgba(255,255,255,0.95)');
1293
+ eyeGrad.addColorStop(0.4, 'rgba(255,255,255,0.5)');
1294
+ eyeGrad.addColorStop(1, 'rgba(255,255,255,0)');
1295
+ ctx.beginPath();
1296
+ ctx.arc(cx + eyeOffsetX, cy + eyeOffsetY, eyeR, 0, Math.PI * 2);
1297
+ ctx.fillStyle = eyeGrad;
1298
+ ctx.fill();
1299
+
1300
+ // --- Floating Thoughts (full only) ---
1301
+ if (!compact) {
1302
+ var now = Date.now();
1303
+ for (var fi = floatingThoughts.length - 1; fi >= 0; fi--) {
1304
+ var ft = floatingThoughts[fi];
1305
+ var age = now - ft.born;
1306
+ if (age > ft.life) {
1307
+ floatingThoughts.splice(fi, 1);
1308
+ continue;
1309
+ }
1310
+ ft.dist += ft.speed * 0.016;
1311
+ var progress = age / ft.life;
1312
+ ft.alpha = progress < 0.15 ? progress / 0.15 : progress > 0.7 ? (1 - progress) / 0.3 : 1;
1313
+ ft.alpha *= 0.7;
1314
+
1315
+ var ftx = cx + Math.cos(ft.angle) * entityR * (1.5 + ft.dist * 2);
1316
+ var fty = cy + Math.sin(ft.angle) * entityR * (1.5 + ft.dist * 2);
1221
1317
 
1222
- function neuralLoop() {
1223
- simulatePhysics();
1318
+ ctx.fillStyle = ft.color + Math.round(ft.alpha * 180).toString(16).padStart(2, '0');
1319
+ ctx.font = '9px ' + getComputedStyle(document.body).fontFamily;
1320
+ ctx.textAlign = 'center';
1321
+ ctx.textBaseline = 'middle';
1322
+ ctx.fillText(ft.text, ftx, fty);
1323
+ }
1324
+ }
1224
1325
 
1225
- var oc = document.getElementById('overviewCanvas');
1226
- if (oc) {
1227
- if (oc.width !== oc.parentElement.clientWidth) oc.width = oc.parentElement.clientWidth;
1228
- drawNeuralCanvas('overviewCanvas', true);
1326
+ // --- Labels ---
1327
+ var moodInfo = MOOD_COLORS[em.mood] || MOOD_COLORS.reflective;
1328
+ if (compact) {
1329
+ var label = document.getElementById('entityMoodLabel');
1330
+ if (label) label.textContent = moodInfo.name + ' (' + Math.round((em.score || 0.5) * 100) + '%)';
1331
+ } else {
1332
+ var moodEl = document.getElementById('entityMoodFull');
1333
+ var scoreEl = document.getElementById('entityScoreFull');
1334
+ if (moodEl) { moodEl.textContent = moodInfo.name; moodEl.style.color = rgba(entityColor, 1); }
1335
+ if (scoreEl) scoreEl.textContent = 'Score: ' + Math.round((em.score || 0.5) * 100) + '% | Valence: ' + (em.valence || 0).toFixed(2) + ' | Arousal: ' + (em.arousal || 0).toFixed(2);
1336
+
1337
+ // Dimension chips
1338
+ var DIM_NAMES = ['frustration','curiosity','surprise','confidence','satisfaction','stress','momentum','creativity'];
1339
+ var DIM_COLS = ['#ff3d3d','#40c4ff','#ffab40','#00ff88','#69f0ae','#ff6e40','#b2ff59','#ea80fc'];
1340
+ var dimEl = document.getElementById('entityDimensions');
1341
+ if (dimEl && dimEl.childElementCount === 0) {
1342
+ var chipHtml = '';
1343
+ for (var ci = 0; ci < DIM_NAMES.length; ci++) {
1344
+ chipHtml += '<div style="background:rgba(10,14,26,0.7);border:1px solid ' + DIM_COLS[ci] + '30;border-radius:6px;padding:4px 10px;font-size:9px;display:flex;gap:6px;align-items:center">' +
1345
+ '<span style="color:' + DIM_COLS[ci] + ';text-transform:uppercase;letter-spacing:1px">' + DIM_NAMES[ci].substring(0, 5) + '</span>' +
1346
+ '<span id="dim-' + DIM_NAMES[ci] + '" style="color:var(--text-dim)">0%</span></div>';
1347
+ }
1348
+ dimEl.innerHTML = chipHtml;
1349
+ }
1350
+ // Update dimension values
1351
+ for (var ui = 0; ui < DIM_NAMES.length; ui++) {
1352
+ var dv = document.getElementById('dim-' + DIM_NAMES[ui]);
1353
+ if (dv) dv.textContent = Math.round((dim[DIM_NAMES[ui]] || 0) * 100) + '%';
1354
+ }
1229
1355
  }
1356
+ }
1357
+
1358
+ function entityLoop() {
1359
+ // Draw preview (always visible on overview)
1360
+ drawEntity('entityPreviewCanvas', true);
1230
1361
 
1231
- var nc = document.getElementById('neuralCanvas');
1232
- if (nc && nc.offsetParent !== null) drawNeuralCanvas('neuralCanvas', false);
1362
+ // Draw full (only when consciousness page visible)
1363
+ var fc = document.getElementById('entityFullCanvas');
1364
+ if (fc && fc.offsetParent !== null) drawEntity('entityFullCanvas', false);
1233
1365
 
1234
- neuralAnimId = requestAnimationFrame(neuralLoop);
1366
+ requestAnimationFrame(entityLoop);
1235
1367
  }
1236
1368
 
1237
1369
  // ═══ SYNTAX HIGHLIGHTING ═══
@@ -1265,13 +1397,12 @@ setInterval(function() {
1265
1397
  }, 10000);
1266
1398
 
1267
1399
  // ═══ BOOT ═══
1268
- initNeuralGraph('overviewCanvas');
1269
- initNeuralGraph('neuralCanvas');
1270
- window.addEventListener('resize', resizeNeuralCanvas);
1400
+ resizeEntityCanvas();
1401
+ window.addEventListener('resize', resizeEntityCanvas);
1271
1402
 
1272
1403
  connect();
1273
1404
  fetchInitial();
1274
- neuralLoop();
1405
+ entityLoop();
1275
1406
  </script>
1276
1407
  </body>
1277
1408
  </html>