@timmeck/brain 1.9.0 → 2.1.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.
Files changed (253) hide show
  1. package/README.md +33 -0
  2. package/brain.log +3876 -0
  3. package/{src/cli/commands/dashboard.ts → dashboard.html} +694 -807
  4. package/dist/api/server.d.ts +4 -18
  5. package/dist/api/server.js +4 -173
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/brain.d.ts +2 -0
  8. package/dist/brain.js +15 -4
  9. package/dist/brain.js.map +1 -1
  10. package/dist/cli/colors.d.ts +4 -25
  11. package/dist/cli/colors.js +3 -89
  12. package/dist/cli/colors.js.map +1 -1
  13. package/dist/cli/commands/dashboard.js +21 -2
  14. package/dist/cli/commands/dashboard.js.map +1 -1
  15. package/dist/cli/commands/peers.d.ts +2 -0
  16. package/dist/cli/commands/peers.js +38 -0
  17. package/dist/cli/commands/peers.js.map +1 -0
  18. package/dist/cli/commands/status.js +0 -1
  19. package/dist/cli/commands/status.js.map +1 -1
  20. package/dist/config.js +2 -29
  21. package/dist/config.js.map +1 -1
  22. package/dist/db/connection.d.ts +1 -2
  23. package/dist/db/connection.js +1 -18
  24. package/dist/db/connection.js.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  28. package/dist/ipc/__tests__/protocol.test.js +117 -0
  29. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  30. package/dist/ipc/client.d.ts +1 -16
  31. package/dist/ipc/client.js +1 -100
  32. package/dist/ipc/client.js.map +1 -1
  33. package/dist/ipc/protocol.d.ts +1 -8
  34. package/dist/ipc/protocol.js +1 -28
  35. package/dist/ipc/protocol.js.map +1 -1
  36. package/dist/ipc/router.d.ts +2 -0
  37. package/dist/ipc/router.js +30 -0
  38. package/dist/ipc/router.js.map +1 -1
  39. package/dist/ipc/server.d.ts +1 -22
  40. package/dist/ipc/server.js +1 -163
  41. package/dist/ipc/server.js.map +1 -1
  42. package/dist/learning/confidence-scorer.d.ts +2 -5
  43. package/dist/learning/confidence-scorer.js +4 -19
  44. package/dist/learning/confidence-scorer.js.map +1 -1
  45. package/dist/learning/decay.js +2 -3
  46. package/dist/learning/decay.js.map +1 -1
  47. package/dist/learning/learning-engine.d.ts +2 -5
  48. package/dist/learning/learning-engine.js +3 -15
  49. package/dist/learning/learning-engine.js.map +1 -1
  50. package/dist/mcp/http-server.d.ts +1 -7
  51. package/dist/mcp/http-server.js +6 -117
  52. package/dist/mcp/http-server.js.map +1 -1
  53. package/dist/mcp/server.js +5 -61
  54. package/dist/mcp/server.js.map +1 -1
  55. package/dist/mcp/tools.js +36 -0
  56. package/dist/mcp/tools.js.map +1 -1
  57. package/dist/parsing/parsers/compiler.js +1 -1
  58. package/dist/parsing/parsers/compiler.js.map +1 -1
  59. package/dist/research/research-engine.d.ts +2 -6
  60. package/dist/research/research-engine.js +3 -23
  61. package/dist/research/research-engine.js.map +1 -1
  62. package/dist/services/synapse.service.d.ts +3 -3
  63. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  64. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  65. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  66. package/dist/synapses/activation.d.ts +3 -13
  67. package/dist/synapses/activation.js +2 -49
  68. package/dist/synapses/activation.js.map +1 -1
  69. package/dist/synapses/decay.d.ts +2 -11
  70. package/dist/synapses/decay.js +2 -26
  71. package/dist/synapses/decay.js.map +1 -1
  72. package/dist/synapses/hebbian.d.ts +2 -13
  73. package/dist/synapses/hebbian.js +2 -35
  74. package/dist/synapses/hebbian.js.map +1 -1
  75. package/dist/synapses/pathfinder.d.ts +2 -14
  76. package/dist/synapses/pathfinder.js +2 -49
  77. package/dist/synapses/pathfinder.js.map +1 -1
  78. package/dist/synapses/synapse-manager.d.ts +7 -23
  79. package/dist/synapses/synapse-manager.js +6 -63
  80. package/dist/synapses/synapse-manager.js.map +1 -1
  81. package/dist/types/ipc.types.d.ts +1 -11
  82. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  83. package/dist/utils/__tests__/hash.test.js +32 -0
  84. package/dist/utils/__tests__/hash.test.js.map +1 -0
  85. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  86. package/dist/utils/__tests__/paths.test.js +75 -0
  87. package/dist/utils/__tests__/paths.test.js.map +1 -0
  88. package/dist/utils/events.d.ts +4 -8
  89. package/dist/utils/events.js +2 -14
  90. package/dist/utils/events.js.map +1 -1
  91. package/dist/utils/hash.d.ts +1 -1
  92. package/dist/utils/hash.js +1 -4
  93. package/dist/utils/hash.js.map +1 -1
  94. package/dist/utils/logger.d.ts +3 -2
  95. package/dist/utils/logger.js +8 -35
  96. package/dist/utils/logger.js.map +1 -1
  97. package/dist/utils/paths.d.ts +2 -1
  98. package/dist/utils/paths.js +4 -13
  99. package/dist/utils/paths.js.map +1 -1
  100. package/eslint.config.js +14 -0
  101. package/package.json +56 -49
  102. package/BRAIN_PLAN.md +0 -3324
  103. package/reddit_post.md +0 -45
  104. package/src/api/server.ts +0 -395
  105. package/src/brain.ts +0 -313
  106. package/src/cli/colors.ts +0 -116
  107. package/src/cli/commands/config.ts +0 -169
  108. package/src/cli/commands/doctor.ts +0 -124
  109. package/src/cli/commands/explain.ts +0 -83
  110. package/src/cli/commands/export.ts +0 -31
  111. package/src/cli/commands/import.ts +0 -199
  112. package/src/cli/commands/insights.ts +0 -65
  113. package/src/cli/commands/learn.ts +0 -24
  114. package/src/cli/commands/modules.ts +0 -53
  115. package/src/cli/commands/network.ts +0 -67
  116. package/src/cli/commands/projects.ts +0 -42
  117. package/src/cli/commands/query.ts +0 -120
  118. package/src/cli/commands/start.ts +0 -105
  119. package/src/cli/commands/status.ts +0 -75
  120. package/src/cli/commands/stop.ts +0 -34
  121. package/src/cli/ipc-helper.ts +0 -22
  122. package/src/cli/update-check.ts +0 -63
  123. package/src/code/analyzer.ts +0 -117
  124. package/src/code/fingerprint.ts +0 -87
  125. package/src/code/matcher.ts +0 -129
  126. package/src/code/parsers/generic.ts +0 -29
  127. package/src/code/parsers/python.ts +0 -54
  128. package/src/code/parsers/typescript.ts +0 -65
  129. package/src/code/registry.ts +0 -60
  130. package/src/code/scorer.ts +0 -120
  131. package/src/config.ts +0 -135
  132. package/src/dashboard/server.ts +0 -142
  133. package/src/db/connection.ts +0 -22
  134. package/src/db/migrations/001_core_schema.ts +0 -120
  135. package/src/db/migrations/002_learning_schema.ts +0 -38
  136. package/src/db/migrations/003_code_schema.ts +0 -53
  137. package/src/db/migrations/004_synapses_schema.ts +0 -57
  138. package/src/db/migrations/005_fts_indexes.ts +0 -78
  139. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  140. package/src/db/migrations/007_feedback.ts +0 -13
  141. package/src/db/migrations/008_git_integration.ts +0 -38
  142. package/src/db/migrations/009_embeddings.ts +0 -8
  143. package/src/db/migrations/index.ts +0 -70
  144. package/src/db/repositories/antipattern.repository.ts +0 -66
  145. package/src/db/repositories/code-module.repository.ts +0 -142
  146. package/src/db/repositories/error.repository.ts +0 -189
  147. package/src/db/repositories/insight.repository.ts +0 -99
  148. package/src/db/repositories/notification.repository.ts +0 -66
  149. package/src/db/repositories/project.repository.ts +0 -93
  150. package/src/db/repositories/rule.repository.ts +0 -108
  151. package/src/db/repositories/solution.repository.ts +0 -154
  152. package/src/db/repositories/synapse.repository.ts +0 -163
  153. package/src/db/repositories/terminal.repository.ts +0 -101
  154. package/src/embeddings/engine.ts +0 -238
  155. package/src/hooks/post-tool-use.ts +0 -92
  156. package/src/hooks/post-write.ts +0 -129
  157. package/src/index.ts +0 -63
  158. package/src/ipc/client.ts +0 -118
  159. package/src/ipc/protocol.ts +0 -35
  160. package/src/ipc/router.ts +0 -133
  161. package/src/ipc/server.ts +0 -176
  162. package/src/learning/confidence-scorer.ts +0 -80
  163. package/src/learning/decay.ts +0 -46
  164. package/src/learning/learning-engine.ts +0 -170
  165. package/src/learning/pattern-extractor.ts +0 -90
  166. package/src/learning/rule-generator.ts +0 -74
  167. package/src/main.rs:10:5 +0 -0
  168. package/src/matching/error-matcher.ts +0 -166
  169. package/src/matching/fingerprint.ts +0 -34
  170. package/src/matching/similarity.ts +0 -61
  171. package/src/matching/tfidf.ts +0 -74
  172. package/src/matching/tokenizer.ts +0 -41
  173. package/src/mcp/auto-detect.ts +0 -93
  174. package/src/mcp/http-server.ts +0 -140
  175. package/src/mcp/server.ts +0 -73
  176. package/src/mcp/tools.ts +0 -328
  177. package/src/parsing/error-parser.ts +0 -28
  178. package/src/parsing/parsers/compiler.ts +0 -93
  179. package/src/parsing/parsers/generic.ts +0 -28
  180. package/src/parsing/parsers/go.ts +0 -97
  181. package/src/parsing/parsers/node.ts +0 -69
  182. package/src/parsing/parsers/python.ts +0 -62
  183. package/src/parsing/parsers/rust.ts +0 -50
  184. package/src/parsing/parsers/shell.ts +0 -42
  185. package/src/parsing/types.ts +0 -47
  186. package/src/research/gap-analyzer.ts +0 -135
  187. package/src/research/insight-generator.ts +0 -123
  188. package/src/research/research-engine.ts +0 -116
  189. package/src/research/synergy-detector.ts +0 -126
  190. package/src/research/template-extractor.ts +0 -130
  191. package/src/research/trend-analyzer.ts +0 -127
  192. package/src/services/analytics.service.ts +0 -226
  193. package/src/services/code.service.ts +0 -271
  194. package/src/services/error.service.ts +0 -266
  195. package/src/services/git.service.ts +0 -132
  196. package/src/services/notification.service.ts +0 -41
  197. package/src/services/prevention.service.ts +0 -159
  198. package/src/services/research.service.ts +0 -98
  199. package/src/services/solution.service.ts +0 -174
  200. package/src/services/synapse.service.ts +0 -59
  201. package/src/services/terminal.service.ts +0 -81
  202. package/src/synapses/activation.ts +0 -80
  203. package/src/synapses/decay.ts +0 -38
  204. package/src/synapses/hebbian.ts +0 -69
  205. package/src/synapses/pathfinder.ts +0 -81
  206. package/src/synapses/synapse-manager.ts +0 -113
  207. package/src/types/code.types.ts +0 -52
  208. package/src/types/config.types.ts +0 -103
  209. package/src/types/error.types.ts +0 -67
  210. package/src/types/ipc.types.ts +0 -8
  211. package/src/types/mcp.types.ts +0 -53
  212. package/src/types/research.types.ts +0 -28
  213. package/src/types/solution.types.ts +0 -30
  214. package/src/types/synapse.types.ts +0 -50
  215. package/src/utils/events.ts +0 -45
  216. package/src/utils/hash.ts +0 -5
  217. package/src/utils/logger.ts +0 -48
  218. package/src/utils/paths.ts +0 -19
  219. package/tests/e2e/test_code_intelligence.py +0 -1015
  220. package/tests/e2e/test_error_memory.py +0 -451
  221. package/tests/e2e/test_full_integration.py +0 -534
  222. package/tests/fixtures/code-modules/modules.ts +0 -83
  223. package/tests/fixtures/errors/go.ts +0 -9
  224. package/tests/fixtures/errors/node.ts +0 -24
  225. package/tests/fixtures/errors/python.ts +0 -21
  226. package/tests/fixtures/errors/rust.ts +0 -25
  227. package/tests/fixtures/errors/shell.ts +0 -15
  228. package/tests/fixtures/solutions/solutions.ts +0 -27
  229. package/tests/helpers/setup-db.ts +0 -52
  230. package/tests/integration/code-flow.test.ts +0 -86
  231. package/tests/integration/error-flow.test.ts +0 -83
  232. package/tests/integration/ipc-flow.test.ts +0 -166
  233. package/tests/integration/learning-cycle.test.ts +0 -82
  234. package/tests/integration/synapse-flow.test.ts +0 -117
  235. package/tests/unit/code/analyzer.test.ts +0 -58
  236. package/tests/unit/code/fingerprint.test.ts +0 -51
  237. package/tests/unit/code/scorer.test.ts +0 -55
  238. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  239. package/tests/unit/learning/decay.test.ts +0 -45
  240. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  241. package/tests/unit/matching/error-matcher.test.ts +0 -69
  242. package/tests/unit/matching/fingerprint.test.ts +0 -47
  243. package/tests/unit/matching/similarity.test.ts +0 -65
  244. package/tests/unit/matching/tfidf.test.ts +0 -71
  245. package/tests/unit/matching/tokenizer.test.ts +0 -83
  246. package/tests/unit/parsing/parsers.test.ts +0 -113
  247. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  248. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  249. package/tests/unit/synapses/activation.test.ts +0 -80
  250. package/tests/unit/synapses/decay.test.ts +0 -27
  251. package/tests/unit/synapses/hebbian.test.ts +0 -96
  252. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  253. package/tsconfig.json +0 -18
@@ -1,807 +1,694 @@
1
- import { Command } from 'commander';
2
- import { withIpc } from '../ipc-helper.js';
3
- import { writeFileSync } from 'fs';
4
- import { resolve } from 'path';
5
- import { c, icons } from '../colors.js';
6
-
7
- export function dashboardCommand(): Command {
8
- return new Command('dashboard')
9
- .description('Generate and open the Brain dashboard with live data')
10
- .option('-o, --output <path>', 'Output HTML file path')
11
- .option('--no-open', 'Generate without opening in browser')
12
- .option('-l, --live', 'Start live dashboard server with SSE updates')
13
- .option('-p, --port <number>', 'Port for live dashboard', '7420')
14
- .action(async (opts) => {
15
- await withIpc(async (client) => {
16
- console.log(`${icons.chart} ${c.info('Fetching data from Brain...')}`);
17
-
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- const summary: any = await client.request('analytics.summary', {});
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- const network: any = await client.request('synapse.stats', {});
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
- const networkOverview: any = await client.request('analytics.network', { limit: 90 });
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- const insights: any = await client.request('research.insights', {
26
- activeOnly: true,
27
- limit: 500,
28
- });
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- const modules: any = await client.request('code.modules', {});
31
-
32
- // Collect language stats
33
- const langStats: Record<string, number> = {};
34
- const projectSet = new Set<string>();
35
- if (Array.isArray(modules)) {
36
- for (const m of modules) {
37
- langStats[m.language] = (langStats[m.language] || 0) + 1;
38
- if (m.projectId) projectSet.add(String(m.projectId));
39
- }
40
- }
41
-
42
- // Categorize insights
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- const insightList = Array.isArray(insights) ? insights : [];
45
- const templates = insightList.filter((i: InsightItem) => i.type === 'template_candidate' || i.title?.includes('Template'));
46
- const suggestions = insightList.filter((i: InsightItem) => i.type === 'suggestion' || i.type === 'project_suggestion');
47
- const trends = insightList.filter((i: InsightItem) => i.type === 'trend' || i.type === 'pattern');
48
- const gaps = insightList.filter((i: InsightItem) => i.type === 'gap');
49
- const warnings = insightList.filter((i: InsightItem) => i.type === 'warning');
50
- const synergies = insightList.filter((i: InsightItem) => i.type === 'synergy' || i.type === 'optimization');
51
-
52
- // Build synapse graph data
53
- const synapseEdges = Array.isArray(networkOverview?.strongestSynapses) ? networkOverview.strongestSynapses : [];
54
-
55
- const data = {
56
- stats: {
57
- modules: summary.modules?.total ?? 0,
58
- synapses: network.totalSynapses ?? 0,
59
- errors: summary.errors?.total ?? 0,
60
- solutions: summary.solutions?.total ?? 0,
61
- rules: summary.rules?.active ?? 0,
62
- insights: insightList.length,
63
- },
64
- langStats,
65
- insights: { templates, suggestions, trends, gaps, warnings, synergies },
66
- synapseEdges,
67
- };
68
-
69
- const html = generateHtml(data);
70
- const outPath = opts.output
71
- ? resolve(opts.output)
72
- : resolve(import.meta.dirname, '../../../dashboard.html');
73
-
74
- // Inject live SSE connection for --live mode
75
- let finalHtml = html;
76
- if (opts.live) {
77
- const apiPort = opts.port || '7777';
78
- const sseScript = `
79
- <script>
80
- (function(){
81
- const evtSource = new EventSource('http://localhost:${apiPort}/api/v1/events');
82
- evtSource.onmessage = function(e) {
83
- try {
84
- const data = JSON.parse(e.data);
85
- if (data.type === 'stats_update') {
86
- document.querySelectorAll('.stat-card').forEach(card => {
87
- const label = card.querySelector('.stat-label')?.textContent?.toLowerCase();
88
- const num = card.querySelector('.stat-number');
89
- if (label && num && data.stats[label] !== undefined) {
90
- num.textContent = Number(data.stats[label]).toLocaleString();
91
- }
92
- });
93
- }
94
- if (data.type === 'event') {
95
- const dot = document.querySelector('.activity-dot');
96
- if (dot) { dot.style.background = '#ff5577'; setTimeout(() => dot.style.background = '', 500); }
97
- }
98
- } catch {}
99
- };
100
- evtSource.onerror = function() { setTimeout(() => location.reload(), 5000); };
101
- })();
102
- </script>`;
103
- finalHtml = html.replace('</body>', sseScript + '</body>');
104
- }
105
-
106
- writeFileSync(outPath, finalHtml, 'utf-8');
107
- console.log(`${icons.ok} ${c.success('Dashboard written to')} ${c.dim(outPath)}`);
108
- if (opts.live) {
109
- console.log(` ${c.info('Live mode:')} Connected to Brain daemon SSE on port ${opts.port || 7777}`);
110
- }
111
- console.log(` ${c.label('Modules:')} ${c.value(data.stats.modules)} ${c.label('Synapses:')} ${c.value(data.stats.synapses)} ${c.label('Insights:')} ${c.value(data.stats.insights)}`);
112
-
113
- if (opts.open !== false) {
114
- const { exec } = await import('child_process');
115
- exec(`start "" "${outPath}"`);
116
- }
117
- });
118
- });
119
- }
120
-
121
- interface InsightItem {
122
- type: string;
123
- title: string;
124
- description?: string;
125
- priority?: string;
126
- }
127
-
128
- interface SynapseEdge {
129
- source: string;
130
- target: string;
131
- type: string;
132
- weight: number;
133
- }
134
-
135
- interface DashboardData {
136
- stats: {
137
- modules: number;
138
- synapses: number;
139
- errors: number;
140
- solutions: number;
141
- rules: number;
142
- insights: number;
143
- };
144
- langStats: Record<string, number>;
145
- insights: {
146
- templates: InsightItem[];
147
- suggestions: InsightItem[];
148
- trends: InsightItem[];
149
- gaps: InsightItem[];
150
- warnings: InsightItem[];
151
- synergies: InsightItem[];
152
- };
153
- synapseEdges: SynapseEdge[];
154
- }
155
-
156
- function esc(s: string): string {
157
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
158
- }
159
-
160
- function generateHtml(data: DashboardData): string {
161
- const { stats, langStats, insights, synapseEdges } = data;
162
-
163
- // Build language chart bars
164
- const sortedLangs = Object.entries(langStats).sort((a, b) => b[1] - a[1]);
165
- const maxLang = sortedLangs[0]?.[1] || 1;
166
- const langBars = sortedLangs.slice(0, 12).map(([lang, count]) => {
167
- const pct = Math.round((count / maxLang) * 100);
168
- return `<div class="lang-row"><span class="lang-name">${esc(lang)}</span><div class="lang-bar-bg"><div class="lang-bar" data-width="${pct}"></div></div><span class="lang-count">${count}</span></div>`;
169
- }).join('\n');
170
-
171
- // Build insight cards
172
- function insightCards(items: InsightItem[], color: string): string {
173
- if (!items.length) return '<p class="empty">Keine Insights in dieser Kategorie.</p>';
174
- return items.slice(0, 30).map(i => {
175
- const prio = i.priority ? `<span class="prio prio-${String(i.priority).toLowerCase()}">${esc(String(i.priority))}</span>` : '';
176
- return `<div class="insight-card ${color}"><div class="insight-header">${prio}<strong>${esc(i.title)}</strong></div><p>${esc((i.description || '').slice(0, 200))}</p></div>`;
177
- }).join('\n');
178
- }
179
-
180
- const totalKnowledge = stats.modules + stats.synapses + stats.errors + stats.solutions;
181
- const activityLevel = Math.min(100, Math.round((stats.insights / Math.max(1, totalKnowledge)) * 1000));
182
-
183
- return `<!DOCTYPE html>
184
- <html lang="de">
185
- <head>
186
- <meta charset="UTF-8">
187
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
188
- <title>Brain — Dashboard</title>
189
- <style>
190
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
191
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
192
- :root{
193
- --bg:#04060e;--bg2:rgba(10,12,24,.7);--bg3:rgba(20,24,50,.6);--bg4:rgba(30,35,70,.5);
194
- --glass:rgba(15,18,40,.55);--glass-border:rgba(100,120,255,.12);--glass-hover:rgba(100,120,255,.2);
195
- --text:#e8eaf6;--text2:#8b8fb0;--text3:#4a4d6e;
196
- --blue:#5b9cff;--red:#ff5577;--green:#3dffa0;
197
- --purple:#b47aff;--orange:#ffb347;--cyan:#47e5ff;
198
- --accent:linear-gradient(135deg,#b47aff,#5b9cff,#47e5ff);
199
- --radius:16px;--radius-sm:10px;
200
- }
201
- html{scroll-behavior:smooth}
202
- body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;min-height:100vh;overflow-x:hidden}
203
-
204
- /* Neural canvas background */
205
- #neural-bg{position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none}
206
-
207
- /* Ambient glow orbs */
208
- .orb{position:fixed;border-radius:50%;filter:blur(120px);opacity:.12;pointer-events:none;z-index:0}
209
- .orb-1{width:600px;height:600px;background:var(--purple);top:-200px;left:-100px;animation:orb-float 20s ease-in-out infinite}
210
- .orb-2{width:500px;height:500px;background:var(--blue);bottom:-150px;right:-100px;animation:orb-float 25s ease-in-out infinite reverse}
211
- .orb-3{width:400px;height:400px;background:var(--cyan);top:40%;left:50%;animation:orb-float 18s ease-in-out infinite 5s}
212
- @keyframes orb-float{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(60px,-40px) scale(1.1)}66%{transform:translate(-40px,60px) scale(.9)}}
213
-
214
- .container{max-width:1400px;margin:0 auto;padding:0 28px;position:relative;z-index:1}
215
-
216
- /* Reveal animations */
217
- .reveal{opacity:0;transform:translateY(30px);transition:opacity .6s ease,transform .6s ease}
218
- .reveal.visible{opacity:1;transform:translateY(0)}
219
- .reveal-delay-1{transition-delay:.1s}.reveal-delay-2{transition-delay:.2s}
220
- .reveal-delay-3{transition-delay:.3s}.reveal-delay-4{transition-delay:.4s}
221
- .reveal-delay-5{transition-delay:.5s}
222
-
223
- section{margin-bottom:56px}
224
-
225
- /* Header */
226
- header{padding:60px 0 24px;text-align:center;position:relative}
227
- .logo{display:flex;align-items:center;justify-content:center;gap:20px;margin-bottom:12px}
228
- .logo-icon{
229
- width:68px;height:68px;border-radius:18px;
230
- background:linear-gradient(135deg,var(--purple),var(--blue),var(--cyan));
231
- display:flex;align-items:center;justify-content:center;font-size:32px;
232
- box-shadow:0 0 60px rgba(170,102,255,.35),0 0 120px rgba(90,150,255,.15);
233
- animation:icon-breathe 4s ease-in-out infinite;
234
- position:relative;
235
- }
236
- .logo-icon::after{
237
- content:'';position:absolute;inset:-3px;border-radius:20px;
238
- background:linear-gradient(135deg,var(--purple),var(--cyan));
239
- opacity:.4;filter:blur(8px);z-index:-1;animation:icon-breathe 4s ease-in-out infinite reverse;
240
- }
241
- @keyframes icon-breathe{0%,100%{box-shadow:0 0 60px rgba(170,102,255,.35),0 0 120px rgba(90,150,255,.15)}50%{box-shadow:0 0 80px rgba(170,102,255,.5),0 0 160px rgba(90,150,255,.25)}}
242
- .logo h1{font-size:2.8rem;font-weight:900;letter-spacing:-1px;background:linear-gradient(135deg,#fff 0%,var(--blue) 50%,var(--purple) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
243
- .tagline{color:var(--text2);font-size:1.05rem;font-weight:300;letter-spacing:.5px}
244
-
245
- /* Activity indicator */
246
- .activity{display:inline-flex;align-items:center;gap:10px;margin-top:16px;padding:8px 20px;border-radius:30px;background:var(--glass);border:1px solid var(--glass-border);backdrop-filter:blur(20px)}
247
- .activity-dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 12px var(--green);animation:pulse-dot 2s ease-in-out infinite}
248
- @keyframes pulse-dot{0%,100%{opacity:1;box-shadow:0 0 12px var(--green)}50%{opacity:.5;box-shadow:0 0 20px var(--green)}}
249
- .activity-text{font-size:.8rem;color:var(--text2);font-weight:500}
250
- .activity-bar{width:80px;height:4px;border-radius:2px;background:var(--bg4);overflow:hidden}
251
- .activity-fill{height:100%;border-radius:2px;background:linear-gradient(90deg,var(--green),var(--cyan));transition:width 1.5s ease}
252
-
253
- /* Nav */
254
- nav{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;padding:20px 0;margin-bottom:40px}
255
- nav a{
256
- color:var(--text2);text-decoration:none;padding:8px 18px;border-radius:24px;font-size:.85rem;font-weight:500;
257
- transition:all .3s ease;border:1px solid transparent;backdrop-filter:blur(10px);
258
- }
259
- nav a:hover{color:var(--text);background:var(--glass);border-color:var(--glass-border);transform:translateY(-1px)}
260
- nav a.research{
261
- background:var(--glass);color:var(--cyan);border-color:rgba(71,229,255,.25);font-weight:600;
262
- box-shadow:0 0 20px rgba(71,229,255,.1);animation:nav-glow 3s ease-in-out infinite alternate;
263
- }
264
- @keyframes nav-glow{0%{box-shadow:0 0 20px rgba(71,229,255,.1)}100%{box-shadow:0 0 35px rgba(71,229,255,.2)}}
265
-
266
- /* Stats */
267
- .stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:18px}
268
- .stat-card{
269
- background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius);
270
- padding:28px 22px;text-align:center;position:relative;overflow:hidden;
271
- transition:all .35s ease;backdrop-filter:blur(20px);
272
- }
273
- .stat-card:hover{transform:translateY(-4px);border-color:var(--glass-hover);box-shadow:0 20px 60px rgba(0,0,0,.3)}
274
- .stat-card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px}
275
- .stat-card::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(ellipse at 50% 0%,rgba(255,255,255,.03),transparent 70%);pointer-events:none}
276
- .stat-card.blue::before{background:linear-gradient(90deg,transparent,var(--blue),transparent)}
277
- .stat-card.purple::before{background:linear-gradient(90deg,transparent,var(--purple),transparent)}
278
- .stat-card.red::before{background:linear-gradient(90deg,transparent,var(--red),transparent)}
279
- .stat-card.green::before{background:linear-gradient(90deg,transparent,var(--green),transparent)}
280
- .stat-card.orange::before{background:linear-gradient(90deg,transparent,var(--orange),transparent)}
281
- .stat-card.cyan::before{background:linear-gradient(90deg,transparent,var(--cyan),transparent)}
282
- .stat-number{font-size:2.6rem;font-weight:900;letter-spacing:-2px}
283
- .stat-card.blue .stat-number{color:var(--blue)}.stat-card.purple .stat-number{color:var(--purple)}
284
- .stat-card.red .stat-number{color:var(--red)}.stat-card.green .stat-number{color:var(--green)}
285
- .stat-card.orange .stat-number{color:var(--orange)}.stat-card.cyan .stat-number{color:var(--cyan)}
286
- .stat-label{color:var(--text2);font-size:.82rem;margin-top:6px;font-weight:500;letter-spacing:.3px;text-transform:uppercase}
287
-
288
- /* Section titles */
289
- .section-title{font-size:1.5rem;font-weight:700;margin-bottom:24px;display:flex;align-items:center;gap:12px}
290
- .section-title .icon{font-size:1.2rem;width:38px;height:38px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(10px)}
291
-
292
- /* Language chart */
293
- .lang-chart{max-width:650px}
294
- .lang-row{display:flex;align-items:center;gap:14px;margin-bottom:10px}
295
- .lang-name{width:100px;text-align:right;font-size:.85rem;color:var(--text2);font-weight:500}
296
- .lang-bar-bg{flex:1;height:28px;background:var(--bg3);border-radius:6px;overflow:hidden;border:1px solid var(--glass-border)}
297
- .lang-bar{height:100%;background:var(--accent);border-radius:6px;width:0;transition:width 1.2s cubic-bezier(.22,1,.36,1)}
298
- .lang-count{width:50px;font-size:.85rem;color:var(--text2);font-weight:600}
299
-
300
- /* Insight tabs */
301
- .tab-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:24px}
302
- .tab-btn{
303
- padding:10px 20px;border-radius:24px;border:1px solid var(--glass-border);
304
- background:var(--glass);color:var(--text2);cursor:pointer;font-size:.85rem;font-weight:500;
305
- transition:all .3s ease;backdrop-filter:blur(10px);font-family:inherit;
306
- }
307
- .tab-btn:hover{border-color:var(--glass-hover);color:var(--text);transform:translateY(-1px)}
308
- .tab-btn.active{border-color:rgba(71,229,255,.35);color:var(--cyan);background:rgba(71,229,255,.08);box-shadow:0 0 20px rgba(71,229,255,.1)}
309
- .tab-btn .count{background:var(--bg4);padding:2px 8px;border-radius:12px;font-size:.72rem;margin-left:6px;font-weight:600}
310
- .tab-panel{display:none}.tab-panel.active{display:block}
311
- .insight-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(380px,1fr));gap:14px}
312
- .insight-card{
313
- background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);
314
- padding:18px;border-left:3px solid var(--text3);transition:all .25s ease;backdrop-filter:blur(20px);
315
- }
316
- .insight-card:hover{transform:translateX(6px);border-color:var(--glass-hover);box-shadow:0 8px 30px rgba(0,0,0,.2)}
317
- .insight-card.cyan{border-left-color:var(--cyan)}.insight-card.orange{border-left-color:var(--orange)}
318
- .insight-card.green{border-left-color:var(--green)}.insight-card.red{border-left-color:var(--red)}
319
- .insight-card.purple{border-left-color:var(--purple)}.insight-card.blue{border-left-color:var(--blue)}
320
- .insight-header{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
321
- .insight-card p{color:var(--text2);font-size:.85rem;line-height:1.5}
322
- .prio{font-size:.68rem;padding:3px 10px;border-radius:12px;text-transform:uppercase;font-weight:700;letter-spacing:.5px}
323
- .prio-critical{background:rgba(255,85,119,.15);color:var(--red);border:1px solid rgba(255,85,119,.25)}
324
- .prio-high{background:rgba(255,179,71,.15);color:var(--orange);border:1px solid rgba(255,179,71,.25)}
325
- .prio-medium{background:rgba(91,156,255,.15);color:var(--blue);border:1px solid rgba(91,156,255,.25)}
326
- .prio-low{background:rgba(139,143,176,.1);color:var(--text2);border:1px solid rgba(139,143,176,.2)}
327
- .empty{color:var(--text3);font-style:italic;padding:24px}
328
-
329
- /* Graph */
330
- .graph-container{position:relative;background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius);overflow:hidden;backdrop-filter:blur(20px)}
331
- #synapse-graph{width:100%;height:650px;display:block;cursor:grab}
332
- #synapse-graph:active{cursor:grabbing}
333
- .graph-legend{display:flex;gap:16px;flex-wrap:wrap;padding:12px 20px;border-top:1px solid var(--glass-border);font-size:.8rem;color:var(--text2)}
334
- .legend-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:6px;vertical-align:middle}
335
- .graph-tooltip{position:absolute;display:none;background:var(--bg2);border:1px solid var(--glass-border);border-radius:8px;padding:8px 14px;font-size:.8rem;color:var(--text);pointer-events:none;z-index:10;backdrop-filter:blur(20px);box-shadow:0 8px 30px rgba(0,0,0,.3)}
336
-
337
- /* Footer */
338
- footer{text-align:center;padding:40px 0;border-top:1px solid var(--glass-border)}
339
- footer p{color:var(--text3);font-size:.8rem}
340
- footer code{background:var(--glass);padding:3px 10px;border-radius:6px;font-size:.78rem;border:1px solid var(--glass-border)}
341
-
342
- /* Responsive */
343
- @media(max-width:600px){.stats-grid{grid-template-columns:1fr 1fr}.insight-grid{grid-template-columns:1fr}.logo h1{font-size:2rem}}
344
- </style>
345
- </head>
346
- <body>
347
-
348
- <canvas id="neural-bg"></canvas>
349
- <div class="orb orb-1"></div>
350
- <div class="orb orb-2"></div>
351
- <div class="orb orb-3"></div>
352
-
353
- <div class="container">
354
- <header class="reveal">
355
- <div class="logo">
356
- <div class="logo-icon">&#129504;</div>
357
- <h1>Brain</h1>
358
- </div>
359
- <p class="tagline">Adaptive Code Intelligence</p>
360
- <div class="activity">
361
- <span class="activity-dot"></span>
362
- <span class="activity-text">Neural Activity</span>
363
- <div class="activity-bar"><div class="activity-fill" style="width:0%" data-target="${activityLevel}"></div></div>
364
- <span class="activity-text" style="color:var(--cyan);font-weight:700">${activityLevel}%</span>
365
- </div>
366
- </header>
367
-
368
- <nav class="reveal reveal-delay-1">
369
- <a href="#stats">Stats</a>
370
- <a href="#languages">Languages</a>
371
- <a href="#network">&#128300; Network</a>
372
- <a href="#research" class="research">&#128161; Research</a>
373
- </nav>
374
-
375
- <section id="stats" class="reveal reveal-delay-2">
376
- <div class="section-title"><div class="icon" style="background:rgba(91,156,255,.1)">&#128202;</div> Neural Status</div>
377
- <div class="stats-grid">
378
- <div class="stat-card blue"><div class="stat-number">${stats.modules.toLocaleString()}</div><div class="stat-label">Modules</div></div>
379
- <div class="stat-card purple"><div class="stat-number">${stats.synapses.toLocaleString()}</div><div class="stat-label">Synapses</div></div>
380
- <div class="stat-card cyan"><div class="stat-number">${stats.insights}</div><div class="stat-label">Insights</div></div>
381
- <div class="stat-card red"><div class="stat-number">${stats.errors}</div><div class="stat-label">Errors</div></div>
382
- <div class="stat-card green"><div class="stat-number">${stats.solutions}</div><div class="stat-label">Solutions</div></div>
383
- <div class="stat-card orange"><div class="stat-number">${stats.rules}</div><div class="stat-label">Rules</div></div>
384
- </div>
385
- </section>
386
-
387
- <section id="languages" class="reveal reveal-delay-3">
388
- <div class="section-title"><div class="icon" style="background:rgba(180,122,255,.1)">&#128187;</div> Languages</div>
389
- <div class="lang-chart">${langBars}</div>
390
- </section>
391
-
392
- <section id="network" class="reveal reveal-delay-4">
393
- <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Synapse Network</div>
394
- <div class="graph-container">
395
- <canvas id="synapse-graph"></canvas>
396
- <div class="graph-legend">
397
- <span><span class="legend-dot" style="background:#ff5577"></span> error</span>
398
- <span><span class="legend-dot" style="background:#3dffa0"></span> solution</span>
399
- <span><span class="legend-dot" style="background:#b47aff"></span> code_module</span>
400
- <span><span class="legend-dot" style="background:#ffb347"></span> project</span>
401
- <span><span class="legend-dot" style="background:#5b9cff"></span> rule</span>
402
- <span style="margin-left:auto;color:var(--text3);font-size:.72rem">edges: co_occurs / solves / uses_module / depends_on</span>
403
- </div>
404
- <div id="graph-tooltip" class="graph-tooltip"></div>
405
- </div>
406
- </section>
407
-
408
- <section id="research" class="reveal reveal-delay-5">
409
- <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Research Insights</div>
410
- <div class="tab-bar">
411
- <button class="tab-btn active" data-tab="templates">&#127912; Templates <span class="count">${insights.templates.length}</span></button>
412
- <button class="tab-btn" data-tab="suggestions">&#128161; Suggestions <span class="count">${insights.suggestions.length}</span></button>
413
- <button class="tab-btn" data-tab="trends">&#128200; Trends <span class="count">${insights.trends.length}</span></button>
414
- <button class="tab-btn" data-tab="gaps">&#9888;&#65039; Gaps <span class="count">${insights.gaps.length}</span></button>
415
- <button class="tab-btn" data-tab="synergies">&#9889; Synergies <span class="count">${insights.synergies.length}</span></button>
416
- <button class="tab-btn" data-tab="warnings">&#128680; Warnings <span class="count">${insights.warnings.length}</span></button>
417
- </div>
418
- <div class="tab-panel active" id="tab-templates"><div class="insight-grid">${insightCards(insights.templates, 'cyan')}</div></div>
419
- <div class="tab-panel" id="tab-suggestions"><div class="insight-grid">${insightCards(insights.suggestions, 'orange')}</div></div>
420
- <div class="tab-panel" id="tab-trends"><div class="insight-grid">${insightCards(insights.trends, 'green')}</div></div>
421
- <div class="tab-panel" id="tab-gaps"><div class="insight-grid">${insightCards(insights.gaps, 'red')}</div></div>
422
- <div class="tab-panel" id="tab-synergies"><div class="insight-grid">${insightCards(insights.synergies, 'purple')}</div></div>
423
- <div class="tab-panel" id="tab-warnings"><div class="insight-grid">${insightCards(insights.warnings, 'red')}</div></div>
424
- </section>
425
-
426
- <footer class="reveal reveal-delay-5">
427
- <p>Brain v1.0 &mdash; <code>brain dashboard</code></p>
428
- </footer>
429
- </div>
430
-
431
- <script>
432
- // --- Neural Network Canvas ---
433
- (function(){
434
- const canvas = document.getElementById('neural-bg');
435
- const ctx = canvas.getContext('2d');
436
- let W, H, nodes = [], mouse = {x:-1000,y:-1000};
437
-
438
- function resize(){
439
- W = canvas.width = window.innerWidth;
440
- H = canvas.height = window.innerHeight;
441
- }
442
- resize();
443
- window.addEventListener('resize', resize);
444
- document.addEventListener('mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; });
445
-
446
- const NODE_COUNT = Math.min(80, Math.floor(window.innerWidth / 18));
447
- const CONNECT_DIST = 180;
448
- const MOUSE_DIST = 200;
449
-
450
- for(let i = 0; i < NODE_COUNT; i++){
451
- nodes.push({
452
- x: Math.random() * W,
453
- y: Math.random() * H,
454
- vx: (Math.random() - 0.5) * 0.4,
455
- vy: (Math.random() - 0.5) * 0.4,
456
- r: Math.random() * 2 + 1,
457
- pulse: Math.random() * Math.PI * 2,
458
- });
459
- }
460
-
461
- function draw(){
462
- ctx.clearRect(0, 0, W, H);
463
-
464
- // Draw connections
465
- for(let i = 0; i < nodes.length; i++){
466
- for(let j = i + 1; j < nodes.length; j++){
467
- const dx = nodes[i].x - nodes[j].x;
468
- const dy = nodes[i].y - nodes[j].y;
469
- const dist = Math.sqrt(dx*dx + dy*dy);
470
- if(dist < CONNECT_DIST){
471
- const alpha = (1 - dist / CONNECT_DIST) * 0.15;
472
- ctx.strokeStyle = 'rgba(91,156,255,' + alpha + ')';
473
- ctx.lineWidth = 0.5;
474
- ctx.beginPath();
475
- ctx.moveTo(nodes[i].x, nodes[i].y);
476
- ctx.lineTo(nodes[j].x, nodes[j].y);
477
- ctx.stroke();
478
- }
479
- }
480
-
481
- // Mouse interaction
482
- const mdx = nodes[i].x - mouse.x;
483
- const mdy = nodes[i].y - mouse.y;
484
- const mDist = Math.sqrt(mdx*mdx + mdy*mdy);
485
- if(mDist < MOUSE_DIST){
486
- const alpha = (1 - mDist / MOUSE_DIST) * 0.4;
487
- ctx.strokeStyle = 'rgba(180,122,255,' + alpha + ')';
488
- ctx.lineWidth = 1;
489
- ctx.beginPath();
490
- ctx.moveTo(nodes[i].x, nodes[i].y);
491
- ctx.lineTo(mouse.x, mouse.y);
492
- ctx.stroke();
493
- }
494
- }
495
-
496
- // Draw nodes
497
- const time = Date.now() * 0.001;
498
- for(const n of nodes){
499
- const glow = 0.4 + Math.sin(time * 1.5 + n.pulse) * 0.3;
500
- ctx.fillStyle = 'rgba(91,156,255,' + glow + ')';
501
- ctx.beginPath();
502
- ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2);
503
- ctx.fill();
504
-
505
- n.x += n.vx;
506
- n.y += n.vy;
507
- if(n.x < 0 || n.x > W) n.vx *= -1;
508
- if(n.y < 0 || n.y > H) n.vy *= -1;
509
- }
510
-
511
- requestAnimationFrame(draw);
512
- }
513
- draw();
514
- })();
515
-
516
- // --- Reveal on scroll ---
517
- const observer = new IntersectionObserver(entries => {
518
- entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
519
- }, {threshold: 0.1});
520
- document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
521
-
522
- // --- Tab switching ---
523
- document.querySelectorAll('.tab-btn').forEach(btn => {
524
- btn.addEventListener('click', () => {
525
- document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
526
- document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
527
- btn.classList.add('active');
528
- document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
529
- });
530
- });
531
-
532
- // --- Animate stat numbers ---
533
- const numObserver = new IntersectionObserver(entries => {
534
- entries.forEach(e => {
535
- if(!e.isIntersecting) return;
536
- const el = e.target;
537
- if(el.dataset.animated) return;
538
- el.dataset.animated = '1';
539
- const target = parseInt(el.textContent.replace(/\\D/g,''), 10);
540
- if(isNaN(target) || target === 0) return;
541
- const duration = 1200;
542
- const start = performance.now();
543
- function tick(now){
544
- const t = Math.min((now - start) / duration, 1);
545
- const ease = 1 - Math.pow(1 - t, 3);
546
- el.textContent = Math.round(target * ease).toLocaleString();
547
- if(t < 1) requestAnimationFrame(tick);
548
- }
549
- requestAnimationFrame(tick);
550
- });
551
- }, {threshold: 0.5});
552
- document.querySelectorAll('.stat-number').forEach(el => numObserver.observe(el));
553
-
554
- // --- Animate language bars ---
555
- setTimeout(() => {
556
- document.querySelectorAll('.lang-bar').forEach(bar => {
557
- bar.style.width = bar.dataset.width + '%';
558
- });
559
- }, 300);
560
-
561
- // --- Activity bar ---
562
- setTimeout(() => {
563
- document.querySelectorAll('.activity-fill').forEach(el => {
564
- el.style.width = el.dataset.target + '%';
565
- });
566
- }, 500);
567
-
568
- // --- Synapse Force-Directed Graph (Premium) ---
569
- (function(){
570
- const edges = ${JSON.stringify(synapseEdges.map((e: SynapseEdge) => ({ s: e.source, t: e.target, type: e.type, w: e.weight })))};
571
- const canvas = document.getElementById('synapse-graph');
572
- if (!canvas || !edges.length) return;
573
- const ctx = canvas.getContext('2d');
574
- const container = canvas.parentElement;
575
- let W, H, dpr;
576
- let frame = 0;
577
-
578
- const NODE_COLORS = {
579
- error: '#ff5577', solution: '#3dffa0', code_module: '#b47aff',
580
- project: '#ffb347', rule: '#5b9cff', antipattern: '#ff5577'
581
- };
582
- const EDGE_COLORS = {
583
- co_occurs: ['#5b9cff','#47e5ff'], solves: ['#3dffa0','#5bff8a'],
584
- uses_module: ['#b47aff','#7a5cff'], depends_on: ['#ff5577','#ffb347'],
585
- caused_by: ['#ff5577','#ff8866']
586
- };
587
- const DEFAULT_COLOR = '#47e5ff';
588
-
589
- // Build graph nodes & edges
590
- const nodeMap = new Map();
591
- const graphEdges = [];
592
- for (const e of edges) {
593
- if (!nodeMap.has(e.s)) nodeMap.set(e.s, { id: e.s, type: e.s.split(':')[0], x: 0, y: 0, vx: 0, vy: 0, connections: 0 });
594
- if (!nodeMap.has(e.t)) nodeMap.set(e.t, { id: e.t, type: e.t.split(':')[0], x: 0, y: 0, vx: 0, vy: 0, connections: 0 });
595
- nodeMap.get(e.s).connections++;
596
- nodeMap.get(e.t).connections++;
597
- graphEdges.push({ source: nodeMap.get(e.s), target: nodeMap.get(e.t), type: e.type, weight: e.w });
598
- }
599
- const nodes = [...nodeMap.values()];
600
-
601
- function resize() {
602
- dpr = window.devicePixelRatio || 1;
603
- W = container.clientWidth;
604
- H = 650;
605
- canvas.width = W * dpr;
606
- canvas.height = H * dpr;
607
- canvas.style.width = W + 'px';
608
- canvas.style.height = H + 'px';
609
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
610
- }
611
- resize();
612
- window.addEventListener('resize', resize);
613
-
614
- // Cluster initial positions by type for better layout
615
- const typeGroups = {};
616
- for (const n of nodes) {
617
- if (!typeGroups[n.type]) typeGroups[n.type] = [];
618
- typeGroups[n.type].push(n);
619
- }
620
- const types = Object.keys(typeGroups);
621
- types.forEach((t, i) => {
622
- const angle = (i / types.length) * Math.PI * 2;
623
- const cx = W/2 + Math.cos(angle) * W * 0.2;
624
- const cy = H/2 + Math.sin(angle) * H * 0.2;
625
- for (const n of typeGroups[t]) {
626
- n.x = cx + (Math.random() - 0.5) * W * 0.25;
627
- n.y = cy + (Math.random() - 0.5) * H * 0.25;
628
- }
629
- });
630
-
631
- // Force simulation
632
- const REPULSION = 4000;
633
- const ATTRACTION = 0.006;
634
- const DAMPING = 0.88;
635
- const CENTER_GRAVITY = 0.0015;
636
- let hovered = null;
637
- let dragging = null;
638
- let dragOff = {x:0,y:0};
639
-
640
- function simulate() {
641
- for (let i = 0; i < nodes.length; i++) {
642
- for (let j = i + 1; j < nodes.length; j++) {
643
- let dx = nodes[i].x - nodes[j].x;
644
- let dy = nodes[i].y - nodes[j].y;
645
- let dist = Math.sqrt(dx*dx + dy*dy) || 1;
646
- let force = REPULSION / (dist * dist);
647
- let fx = (dx / dist) * force;
648
- let fy = (dy / dist) * force;
649
- nodes[i].vx += fx; nodes[i].vy += fy;
650
- nodes[j].vx -= fx; nodes[j].vy -= fy;
651
- }
652
- }
653
- for (const e of graphEdges) {
654
- let dx = e.target.x - e.source.x;
655
- let dy = e.target.y - e.source.y;
656
- let dist = Math.sqrt(dx*dx + dy*dy) || 1;
657
- let force = (dist - 120) * ATTRACTION * e.weight;
658
- let fx = (dx / dist) * force;
659
- let fy = (dy / dist) * force;
660
- e.source.vx += fx; e.source.vy += fy;
661
- e.target.vx -= fx; e.target.vy -= fy;
662
- }
663
- for (const n of nodes) {
664
- n.vx += (W/2 - n.x) * CENTER_GRAVITY;
665
- n.vy += (H/2 - n.y) * CENTER_GRAVITY;
666
- }
667
- for (const n of nodes) {
668
- if (n === dragging) continue;
669
- n.vx *= DAMPING; n.vy *= DAMPING;
670
- n.x += n.vx; n.y += n.vy;
671
- n.x = Math.max(30, Math.min(W - 30, n.x));
672
- n.y = Math.max(30, Math.min(H - 30, n.y));
673
- }
674
- }
675
-
676
- function getNodeRadius(n) {
677
- return Math.min(20, 4 + Math.sqrt(n.connections) * 3.5);
678
- }
679
-
680
- function draw() {
681
- frame++;
682
- ctx.clearRect(0, 0, W, H);
683
-
684
- // Edges with gradient colors per type
685
- for (const e of graphEdges) {
686
- const alpha = 0.1 + e.weight * 0.4;
687
- const colors = EDGE_COLORS[e.type] || ['#5b9cff','#47e5ff'];
688
- const grad = ctx.createLinearGradient(e.source.x, e.source.y, e.target.x, e.target.y);
689
- grad.addColorStop(0, colors[0] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
690
- grad.addColorStop(1, colors[1] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
691
- ctx.strokeStyle = grad;
692
- ctx.lineWidth = 0.5 + e.weight * 2.5;
693
- ctx.beginPath();
694
- ctx.moveTo(e.source.x, e.source.y);
695
- ctx.lineTo(e.target.x, e.target.y);
696
- ctx.stroke();
697
- }
698
-
699
- // Nodes with ambient glow + pulse on hubs
700
- for (const n of nodes) {
701
- const r = getNodeRadius(n);
702
- const color = NODE_COLORS[n.type] || DEFAULT_COLOR;
703
- const isHover = n === hovered || n === dragging;
704
- const isHub = n.connections >= 5;
705
-
706
- // Ambient glow for all nodes
707
- const glowSize = isHover ? 30 : (isHub ? 15 + Math.sin(frame * 0.03 + n.x) * 5 : 8);
708
- ctx.shadowColor = color;
709
- ctx.shadowBlur = glowSize;
710
-
711
- // Outer ring for hubs
712
- if (isHub || isHover) {
713
- const pulseR = r + 3 + (isHub ? Math.sin(frame * 0.04 + n.y) * 2 : 0);
714
- ctx.strokeStyle = color;
715
- ctx.globalAlpha = isHover ? 0.6 : 0.25;
716
- ctx.lineWidth = 1.5;
717
- ctx.beginPath();
718
- ctx.arc(n.x, n.y, pulseR, 0, Math.PI * 2);
719
- ctx.stroke();
720
- }
721
-
722
- // Core node
723
- ctx.globalAlpha = isHover ? 1 : 0.85;
724
- ctx.fillStyle = color;
725
- ctx.beginPath();
726
- ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
727
- ctx.fill();
728
-
729
- // Inner highlight (glossy effect)
730
- ctx.globalAlpha = 0.3;
731
- ctx.fillStyle = '#ffffff';
732
- ctx.beginPath();
733
- ctx.arc(n.x - r * 0.25, n.y - r * 0.25, r * 0.4, 0, Math.PI * 2);
734
- ctx.fill();
735
-
736
- ctx.globalAlpha = 1;
737
- ctx.shadowBlur = 0;
738
-
739
- // Labels
740
- if (isHover || n.connections >= 4) {
741
- const label = n.type === 'project' ? n.id.replace('project:','P') : n.id.split(':')[0];
742
- ctx.fillStyle = '#e8eaf6';
743
- ctx.font = (isHover ? 'bold 12px' : '10px') + ' Inter, system-ui, sans-serif';
744
- ctx.textAlign = 'center';
745
- ctx.globalAlpha = isHover ? 1 : 0.7;
746
- ctx.fillText(isHover ? n.id : label, n.x, n.y - r - 8);
747
- ctx.globalAlpha = 1;
748
- }
749
- }
750
- simulate();
751
- requestAnimationFrame(draw);
752
- }
753
- draw();
754
-
755
- // Interaction
756
- const tooltip = document.getElementById('graph-tooltip');
757
- function getNodeAt(mx, my) {
758
- for (let i = nodes.length - 1; i >= 0; i--) {
759
- const n = nodes[i], r = getNodeRadius(n);
760
- if (Math.hypot(mx - n.x, my - n.y) <= r + 4) return n;
761
- }
762
- return null;
763
- }
764
- function getPos(e) {
765
- const rect = canvas.getBoundingClientRect();
766
- return { x: e.clientX - rect.left, y: e.clientY - rect.top };
767
- }
768
- canvas.addEventListener('mousemove', function(e) {
769
- const p = getPos(e);
770
- if (dragging) {
771
- dragging.x = p.x + dragOff.x;
772
- dragging.y = p.y + dragOff.y;
773
- dragging.vx = 0; dragging.vy = 0;
774
- return;
775
- }
776
- const n = getNodeAt(p.x, p.y);
777
- hovered = n;
778
- canvas.style.cursor = n ? 'pointer' : 'grab';
779
- if (n) {
780
- const conns = graphEdges.filter(e => e.source === n || e.target === n);
781
- const types = {};
782
- conns.forEach(c => { types[c.type] = (types[c.type]||0)+1; });
783
- const typeStr = Object.entries(types).map(([t,c]) => t+': '+c).join(', ');
784
- tooltip.innerHTML = '<strong>' + n.id + '</strong><br>' + conns.length + ' connections<br><span style="color:var(--text3);font-size:.75rem">' + typeStr + '</span>';
785
- tooltip.style.display = 'block';
786
- tooltip.style.left = (p.x + 15) + 'px';
787
- tooltip.style.top = (p.y - 10) + 'px';
788
- } else {
789
- tooltip.style.display = 'none';
790
- }
791
- });
792
- canvas.addEventListener('mousedown', function(e) {
793
- const p = getPos(e);
794
- const n = getNodeAt(p.x, p.y);
795
- if (n) {
796
- dragging = n;
797
- dragOff = { x: n.x - p.x, y: n.y - p.y };
798
- canvas.style.cursor = 'grabbing';
799
- }
800
- });
801
- canvas.addEventListener('mouseup', function() { dragging = null; });
802
- canvas.addEventListener('mouseleave', function() { dragging = null; hovered = null; tooltip.style.display = 'none'; });
803
- })();
804
- </script>
805
- </body>
806
- </html>`;
807
- }
1
+ <!DOCTYPE html>
2
+ <html lang="de">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Brain — Dashboard</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
9
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
10
+ :root{
11
+ --bg:#04060e;--bg2:rgba(10,12,24,.7);--bg3:rgba(20,24,50,.6);--bg4:rgba(30,35,70,.5);
12
+ --glass:rgba(15,18,40,.55);--glass-border:rgba(100,120,255,.12);--glass-hover:rgba(100,120,255,.2);
13
+ --text:#e8eaf6;--text2:#8b8fb0;--text3:#4a4d6e;
14
+ --blue:#5b9cff;--red:#ff5577;--green:#3dffa0;
15
+ --purple:#b47aff;--orange:#ffb347;--cyan:#47e5ff;
16
+ --accent:linear-gradient(135deg,#b47aff,#5b9cff,#47e5ff);
17
+ --radius:16px;--radius-sm:10px;
18
+ }
19
+ html{scroll-behavior:smooth}
20
+ body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;min-height:100vh;overflow-x:hidden}
21
+
22
+ /* Neural canvas background */
23
+ #neural-bg{position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none}
24
+
25
+ /* Ambient glow orbs */
26
+ .orb{position:fixed;border-radius:50%;filter:blur(120px);opacity:.12;pointer-events:none;z-index:0}
27
+ .orb-1{width:600px;height:600px;background:var(--purple);top:-200px;left:-100px;animation:orb-float 20s ease-in-out infinite}
28
+ .orb-2{width:500px;height:500px;background:var(--blue);bottom:-150px;right:-100px;animation:orb-float 25s ease-in-out infinite reverse}
29
+ .orb-3{width:400px;height:400px;background:var(--cyan);top:40%;left:50%;animation:orb-float 18s ease-in-out infinite 5s}
30
+ @keyframes orb-float{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(60px,-40px) scale(1.1)}66%{transform:translate(-40px,60px) scale(.9)}}
31
+
32
+ .container{max-width:1400px;margin:0 auto;padding:0 28px;position:relative;z-index:1}
33
+
34
+ /* Reveal animations */
35
+ .reveal{opacity:0;transform:translateY(30px);transition:opacity .6s ease,transform .6s ease}
36
+ .reveal.visible{opacity:1;transform:translateY(0)}
37
+ .reveal-delay-1{transition-delay:.1s}.reveal-delay-2{transition-delay:.2s}
38
+ .reveal-delay-3{transition-delay:.3s}.reveal-delay-4{transition-delay:.4s}
39
+ .reveal-delay-5{transition-delay:.5s}
40
+
41
+ section{margin-bottom:56px}
42
+
43
+ /* Header */
44
+ header{padding:60px 0 24px;text-align:center;position:relative}
45
+ .logo{display:flex;align-items:center;justify-content:center;gap:20px;margin-bottom:12px}
46
+ .logo-icon{
47
+ width:68px;height:68px;border-radius:18px;
48
+ background:linear-gradient(135deg,var(--purple),var(--blue),var(--cyan));
49
+ display:flex;align-items:center;justify-content:center;font-size:32px;
50
+ box-shadow:0 0 60px rgba(170,102,255,.35),0 0 120px rgba(90,150,255,.15);
51
+ animation:icon-breathe 4s ease-in-out infinite;
52
+ position:relative;
53
+ }
54
+ .logo-icon::after{
55
+ content:'';position:absolute;inset:-3px;border-radius:20px;
56
+ background:linear-gradient(135deg,var(--purple),var(--cyan));
57
+ opacity:.4;filter:blur(8px);z-index:-1;animation:icon-breathe 4s ease-in-out infinite reverse;
58
+ }
59
+ @keyframes icon-breathe{0%,100%{box-shadow:0 0 60px rgba(170,102,255,.35),0 0 120px rgba(90,150,255,.15)}50%{box-shadow:0 0 80px rgba(170,102,255,.5),0 0 160px rgba(90,150,255,.25)}}
60
+ .logo h1{font-size:2.8rem;font-weight:900;letter-spacing:-1px;background:linear-gradient(135deg,#fff 0%,var(--blue) 50%,var(--purple) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
61
+ .tagline{color:var(--text2);font-size:1.05rem;font-weight:300;letter-spacing:.5px}
62
+
63
+ /* Activity indicator */
64
+ .activity{display:inline-flex;align-items:center;gap:10px;margin-top:16px;padding:8px 20px;border-radius:30px;background:var(--glass);border:1px solid var(--glass-border);backdrop-filter:blur(20px)}
65
+ .activity-dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 12px var(--green);animation:pulse-dot 2s ease-in-out infinite}
66
+ @keyframes pulse-dot{0%,100%{opacity:1;box-shadow:0 0 12px var(--green)}50%{opacity:.5;box-shadow:0 0 20px var(--green)}}
67
+ .activity-text{font-size:.8rem;color:var(--text2);font-weight:500}
68
+ .activity-bar{width:80px;height:4px;border-radius:2px;background:var(--bg4);overflow:hidden}
69
+ .activity-fill{height:100%;border-radius:2px;background:linear-gradient(90deg,var(--green),var(--cyan));transition:width 1.5s ease}
70
+
71
+ /* Nav */
72
+ nav{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;padding:20px 0;margin-bottom:40px}
73
+ nav a{
74
+ color:var(--text2);text-decoration:none;padding:8px 18px;border-radius:24px;font-size:.85rem;font-weight:500;
75
+ transition:all .3s ease;border:1px solid transparent;backdrop-filter:blur(10px);
76
+ }
77
+ nav a:hover{color:var(--text);background:var(--glass);border-color:var(--glass-border);transform:translateY(-1px)}
78
+ nav a.research{
79
+ background:var(--glass);color:var(--cyan);border-color:rgba(71,229,255,.25);font-weight:600;
80
+ box-shadow:0 0 20px rgba(71,229,255,.1);animation:nav-glow 3s ease-in-out infinite alternate;
81
+ }
82
+ @keyframes nav-glow{0%{box-shadow:0 0 20px rgba(71,229,255,.1)}100%{box-shadow:0 0 35px rgba(71,229,255,.2)}}
83
+
84
+ /* Stats */
85
+ .stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:18px}
86
+ .stat-card{
87
+ background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius);
88
+ padding:28px 22px;text-align:center;position:relative;overflow:hidden;
89
+ transition:all .35s ease;backdrop-filter:blur(20px);
90
+ }
91
+ .stat-card:hover{transform:translateY(-4px);border-color:var(--glass-hover);box-shadow:0 20px 60px rgba(0,0,0,.3)}
92
+ .stat-card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px}
93
+ .stat-card::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(ellipse at 50% 0%,rgba(255,255,255,.03),transparent 70%);pointer-events:none}
94
+ .stat-card.blue::before{background:linear-gradient(90deg,transparent,var(--blue),transparent)}
95
+ .stat-card.purple::before{background:linear-gradient(90deg,transparent,var(--purple),transparent)}
96
+ .stat-card.red::before{background:linear-gradient(90deg,transparent,var(--red),transparent)}
97
+ .stat-card.green::before{background:linear-gradient(90deg,transparent,var(--green),transparent)}
98
+ .stat-card.orange::before{background:linear-gradient(90deg,transparent,var(--orange),transparent)}
99
+ .stat-card.cyan::before{background:linear-gradient(90deg,transparent,var(--cyan),transparent)}
100
+ .stat-number{font-size:2.6rem;font-weight:900;letter-spacing:-2px}
101
+ .stat-card.blue .stat-number{color:var(--blue)}.stat-card.purple .stat-number{color:var(--purple)}
102
+ .stat-card.red .stat-number{color:var(--red)}.stat-card.green .stat-number{color:var(--green)}
103
+ .stat-card.orange .stat-number{color:var(--orange)}.stat-card.cyan .stat-number{color:var(--cyan)}
104
+ .stat-label{color:var(--text2);font-size:.82rem;margin-top:6px;font-weight:500;letter-spacing:.3px;text-transform:uppercase}
105
+
106
+ /* Section titles */
107
+ .section-title{font-size:1.5rem;font-weight:700;margin-bottom:24px;display:flex;align-items:center;gap:12px}
108
+ .section-title .icon{font-size:1.2rem;width:38px;height:38px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(10px)}
109
+
110
+ /* Language chart */
111
+ .lang-chart{max-width:650px}
112
+ .lang-row{display:flex;align-items:center;gap:14px;margin-bottom:10px}
113
+ .lang-name{width:100px;text-align:right;font-size:.85rem;color:var(--text2);font-weight:500}
114
+ .lang-bar-bg{flex:1;height:28px;background:var(--bg3);border-radius:6px;overflow:hidden;border:1px solid var(--glass-border)}
115
+ .lang-bar{height:100%;background:var(--accent);border-radius:6px;width:0;transition:width 1.2s cubic-bezier(.22,1,.36,1)}
116
+ .lang-count{width:50px;font-size:.85rem;color:var(--text2);font-weight:600}
117
+
118
+ /* Insight tabs */
119
+ .tab-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:24px}
120
+ .tab-btn{
121
+ padding:10px 20px;border-radius:24px;border:1px solid var(--glass-border);
122
+ background:var(--glass);color:var(--text2);cursor:pointer;font-size:.85rem;font-weight:500;
123
+ transition:all .3s ease;backdrop-filter:blur(10px);font-family:inherit;
124
+ }
125
+ .tab-btn:hover{border-color:var(--glass-hover);color:var(--text);transform:translateY(-1px)}
126
+ .tab-btn.active{border-color:rgba(71,229,255,.35);color:var(--cyan);background:rgba(71,229,255,.08);box-shadow:0 0 20px rgba(71,229,255,.1)}
127
+ .tab-btn .count{background:var(--bg4);padding:2px 8px;border-radius:12px;font-size:.72rem;margin-left:6px;font-weight:600}
128
+ .tab-panel{display:none}.tab-panel.active{display:block}
129
+ .insight-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(380px,1fr));gap:14px}
130
+ .insight-card{
131
+ background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);
132
+ padding:18px;border-left:3px solid var(--text3);transition:all .25s ease;backdrop-filter:blur(20px);
133
+ }
134
+ .insight-card:hover{transform:translateX(6px);border-color:var(--glass-hover);box-shadow:0 8px 30px rgba(0,0,0,.2)}
135
+ .insight-card.cyan{border-left-color:var(--cyan)}.insight-card.orange{border-left-color:var(--orange)}
136
+ .insight-card.green{border-left-color:var(--green)}.insight-card.red{border-left-color:var(--red)}
137
+ .insight-card.purple{border-left-color:var(--purple)}.insight-card.blue{border-left-color:var(--blue)}
138
+ .insight-header{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
139
+ .insight-card p{color:var(--text2);font-size:.85rem;line-height:1.5}
140
+ .prio{font-size:.68rem;padding:3px 10px;border-radius:12px;text-transform:uppercase;font-weight:700;letter-spacing:.5px}
141
+ .prio-critical{background:rgba(255,85,119,.15);color:var(--red);border:1px solid rgba(255,85,119,.25)}
142
+ .prio-high{background:rgba(255,179,71,.15);color:var(--orange);border:1px solid rgba(255,179,71,.25)}
143
+ .prio-medium{background:rgba(91,156,255,.15);color:var(--blue);border:1px solid rgba(91,156,255,.25)}
144
+ .prio-low{background:rgba(139,143,176,.1);color:var(--text2);border:1px solid rgba(139,143,176,.2)}
145
+ .empty{color:var(--text3);font-style:italic;padding:24px}
146
+
147
+ /* Graph */
148
+ .graph-container{position:relative;background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius);overflow:hidden;backdrop-filter:blur(20px)}
149
+ #synapse-graph{width:100%;height:650px;display:block;cursor:grab}
150
+ #synapse-graph:active{cursor:grabbing}
151
+ .graph-legend{display:flex;gap:16px;flex-wrap:wrap;padding:12px 20px;border-top:1px solid var(--glass-border);font-size:.8rem;color:var(--text2)}
152
+ .legend-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:6px;vertical-align:middle}
153
+ .graph-tooltip{position:absolute;display:none;background:var(--bg2);border:1px solid var(--glass-border);border-radius:8px;padding:8px 14px;font-size:.8rem;color:var(--text);pointer-events:none;z-index:10;backdrop-filter:blur(20px);box-shadow:0 8px 30px rgba(0,0,0,.3)}
154
+
155
+ /* Footer */
156
+ footer{text-align:center;padding:40px 0;border-top:1px solid var(--glass-border)}
157
+ footer p{color:var(--text3);font-size:.8rem}
158
+ footer code{background:var(--glass);padding:3px 10px;border-radius:6px;font-size:.78rem;border:1px solid var(--glass-border)}
159
+
160
+ /* Responsive */
161
+ @media(max-width:600px){.stats-grid{grid-template-columns:1fr 1fr}.insight-grid{grid-template-columns:1fr}.logo h1{font-size:2rem}}
162
+ </style>
163
+ </head>
164
+ <body>
165
+
166
+ <canvas id="neural-bg"></canvas>
167
+ <div class="orb orb-1"></div>
168
+ <div class="orb orb-2"></div>
169
+ <div class="orb orb-3"></div>
170
+
171
+ <div class="container">
172
+ <header class="reveal">
173
+ <div class="logo">
174
+ <div class="logo-icon">&#129504;</div>
175
+ <h1>Brain</h1>
176
+ </div>
177
+ <p class="tagline">Adaptive Code Intelligence</p>
178
+ <div class="activity">
179
+ <span class="activity-dot"></span>
180
+ <span class="activity-text">Neural Activity</span>
181
+ <div class="activity-bar"><div class="activity-fill" style="width:0%" data-target="91"></div></div>
182
+ <span class="activity-text" style="color:var(--cyan);font-weight:700">91%</span>
183
+ </div>
184
+ </header>
185
+
186
+ <nav class="reveal reveal-delay-1">
187
+ <a href="#stats">Stats</a>
188
+ <a href="#ecosystem">Ecosystem</a>
189
+ <a href="#languages">Languages</a>
190
+ <a href="#network">&#128300; Network</a>
191
+ <a href="#research" class="research">&#128161; Research</a>
192
+ </nav>
193
+
194
+ <section id="stats" class="reveal reveal-delay-2">
195
+ <div class="section-title"><div class="icon" style="background:rgba(91,156,255,.1)">&#128202;</div> Neural Status</div>
196
+ <div class="stats-grid">
197
+ <div class="stat-card blue"><div class="stat-number">18.160</div><div class="stat-label">Modules</div></div>
198
+ <div class="stat-card purple"><div class="stat-number">37.277</div><div class="stat-label">Synapses</div></div>
199
+ <div class="stat-card cyan"><div class="stat-number">5031</div><div class="stat-label">Insights</div></div>
200
+ <div class="stat-card red"><div class="stat-number">0</div><div class="stat-label">Errors</div></div>
201
+ <div class="stat-card green"><div class="stat-number">0</div><div class="stat-label">Solutions</div></div>
202
+ <div class="stat-card orange"><div class="stat-number">0</div><div class="stat-label">Rules</div></div>
203
+ </div>
204
+ </section>
205
+
206
+ <section id="ecosystem" class="reveal reveal-delay-3">
207
+ <div class="section-title"><div class="icon" style="background:rgba(61,255,160,.1)">&#127760;</div> Ecosystem Peers</div>
208
+ <div class="stats-grid">{{ECOSYSTEM_PEERS}}</div>
209
+ </section>
210
+
211
+ <section id="languages" class="reveal reveal-delay-3">
212
+ <div class="section-title"><div class="icon" style="background:rgba(180,122,255,.1)">&#128187;</div> Languages</div>
213
+ <div class="lang-chart"><div class="lang-row"><span class="lang-name">python</span><div class="lang-bar-bg"><div class="lang-bar" data-width="100"></div></div><span class="lang-count">10344</span></div>
214
+ <div class="lang-row"><span class="lang-name">c</span><div class="lang-bar-bg"><div class="lang-bar" data-width="52"></div></div><span class="lang-count">5402</span></div>
215
+ <div class="lang-row"><span class="lang-name">typescript</span><div class="lang-bar-bg"><div class="lang-bar" data-width="18"></div></div><span class="lang-count">1865</span></div>
216
+ <div class="lang-row"><span class="lang-name">javascript</span><div class="lang-bar-bg"><div class="lang-bar" data-width="4"></div></div><span class="lang-count">416</span></div>
217
+ <div class="lang-row"><span class="lang-name">cpp</span><div class="lang-bar-bg"><div class="lang-bar" data-width="1"></div></div><span class="lang-count">107</span></div>
218
+ <div class="lang-row"><span class="lang-name">shell</span><div class="lang-bar-bg"><div class="lang-bar" data-width="0"></div></div><span class="lang-count">20</span></div>
219
+ <div class="lang-row"><span class="lang-name">rust</span><div class="lang-bar-bg"><div class="lang-bar" data-width="0"></div></div><span class="lang-count">6</span></div></div>
220
+ </section>
221
+
222
+ <section id="network" class="reveal reveal-delay-4">
223
+ <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Synapse Network</div>
224
+ <div class="graph-container">
225
+ <canvas id="synapse-graph"></canvas>
226
+ <div class="graph-legend">
227
+ <span><span class="legend-dot" style="background:#ff5577"></span> error</span>
228
+ <span><span class="legend-dot" style="background:#3dffa0"></span> solution</span>
229
+ <span><span class="legend-dot" style="background:#b47aff"></span> code_module</span>
230
+ <span><span class="legend-dot" style="background:#ffb347"></span> project</span>
231
+ <span><span class="legend-dot" style="background:#5b9cff"></span> rule</span>
232
+ <span style="margin-left:auto;color:var(--text3);font-size:.72rem">edges: co_occurs / solves / uses_module / depends_on</span>
233
+ </div>
234
+ <div id="graph-tooltip" class="graph-tooltip"></div>
235
+ </div>
236
+ </section>
237
+
238
+ <section id="research" class="reveal reveal-delay-5">
239
+ <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Research Insights</div>
240
+ <div class="tab-bar">
241
+ <button class="tab-btn active" data-tab="templates">&#127912; Templates <span class="count">5031</span></button>
242
+ <button class="tab-btn" data-tab="suggestions">&#128161; Suggestions <span class="count">5031</span></button>
243
+ <button class="tab-btn" data-tab="trends">&#128200; Trends <span class="count">0</span></button>
244
+ <button class="tab-btn" data-tab="gaps">&#9888;&#65039; Gaps <span class="count">0</span></button>
245
+ <button class="tab-btn" data-tab="synergies">&#9889; Synergies <span class="count">0</span></button>
246
+ <button class="tab-btn" data-tab="warnings">&#128680; Warnings <span class="count">0</span></button>
247
+ </div>
248
+ <div class="tab-panel active" id="tab-templates"><div class="insight-grid"><div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: alert.tsx</strong></div><p>&quot;alert.tsx&quot; exists in 4 projects as adaptations (avg reusability 52%). Generalize into parameterized template.</p></div>
249
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: button.tsx</strong></div><p>&quot;button.tsx&quot; exists in 8 projects as adaptations (avg reusability 71%). Generalize into parameterized template.</p></div>
250
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: config.py</strong></div><p>&quot;config.py&quot; exists in 5 projects as adaptations (avg reusability 74%). Generalize into parameterized template.</p></div>
251
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: footer.tsx</strong></div><p>&quot;footer.tsx&quot; exists in 8 projects as adaptations (avg reusability 78%). Generalize into parameterized template.</p></div>
252
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: index.ts</strong></div><p>&quot;index.ts&quot; exists in 12 projects as adaptations (avg reusability 63%). Generalize into parameterized template.</p></div>
253
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: input.tsx</strong></div><p>&quot;input.tsx&quot; exists in 7 projects as adaptations (avg reusability 64%). Generalize into parameterized template.</p></div>
254
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: layout.tsx</strong></div><p>&quot;layout.tsx&quot; exists in 12 projects as adaptations (avg reusability 77%). Generalize into parameterized template.</p></div>
255
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: main.py</strong></div><p>&quot;main.py&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
256
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: page.tsx</strong></div><p>&quot;page.tsx&quot; exists in 12 projects as adaptations (avg reusability 77%). Generalize into parameterized template.</p></div>
257
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: postcss.config.js</strong></div><p>&quot;postcss.config.js&quot; exists in 4 projects as adaptations (avg reusability 53%). Generalize into parameterized template.</p></div>
258
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: route.ts</strong></div><p>&quot;route.ts&quot; exists in 11 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
259
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: run.py</strong></div><p>&quot;run.py&quot; exists in 4 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
260
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: sidebar.tsx</strong></div><p>&quot;sidebar.tsx&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
261
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: tailwind.config.js</strong></div><p>&quot;tailwind.config.js&quot; exists in 4 projects as adaptations (avg reusability 56%). Generalize into parameterized template.</p></div>
262
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-debounce.ts</strong></div><p>&quot;use-debounce.ts&quot; exists in 4 projects as adaptations (avg reusability 87%). Generalize into parameterized template.</p></div>
263
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-local-storage.ts</strong></div><p>&quot;use-local-storage.ts&quot; exists in 4 projects as adaptations (avg reusability 74%). Generalize into parameterized template.</p></div>
264
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-media-query.ts</strong></div><p>&quot;use-media-query.ts&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
265
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: auth.ts</strong></div><p>&quot;auth.ts&quot; exists in 7 projects as adaptations (avg reusability 67%). Generalize into parameterized template.</p></div>
266
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: badge.tsx</strong></div><p>&quot;badge.tsx&quot; exists in 5 projects as adaptations (avg reusability 75%). Generalize into parameterized template.</p></div>
267
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: client.ts</strong></div><p>&quot;client.ts&quot; exists in 6 projects as adaptations (avg reusability 69%). Generalize into parameterized template.</p></div>
268
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: db.ts</strong></div><p>&quot;db.ts&quot; exists in 4 projects as adaptations (avg reusability 73%). Generalize into parameterized template.</p></div>
269
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: error.tsx</strong></div><p>&quot;error.tsx&quot; exists in 4 projects as adaptations (avg reusability 71%). Generalize into parameterized template.</p></div>
270
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: not-found.tsx</strong></div><p>&quot;not-found.tsx&quot; exists in 4 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
271
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: select.tsx</strong></div><p>&quot;select.tsx&quot; exists in 5 projects as adaptations (avg reusability 54%). Generalize into parameterized template.</p></div>
272
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: textarea.tsx</strong></div><p>&quot;textarea.tsx&quot; exists in 4 projects as adaptations (avg reusability 63%). Generalize into parameterized template.</p></div>
273
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: utils.ts</strong></div><p>&quot;utils.ts&quot; exists in 7 projects as adaptations (avg reusability 75%). Generalize into parameterized template.</p></div>
274
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: middleware.ts</strong></div><p>&quot;middleware.ts&quot; exists in 5 projects as adaptations (avg reusability 78%). Generalize into parameterized template.</p></div>
275
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: providers.tsx</strong></div><p>&quot;providers.tsx&quot; exists in 5 projects as adaptations (avg reusability 80%). Generalize into parameterized template.</p></div>
276
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: progress.tsx</strong></div><p>&quot;progress.tsx&quot; exists in 4 projects as adaptations (avg reusability 52%). Generalize into parameterized template.</p></div>
277
+ <div class="insight-card cyan"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: tailwind.config.ts</strong></div><p>&quot;tailwind.config.ts&quot; exists in 4 projects as adaptations (avg reusability 88%). Generalize into parameterized template.</p></div></div></div>
278
+ <div class="tab-panel" id="tab-suggestions"><div class="insight-grid"><div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: alert.tsx</strong></div><p>&quot;alert.tsx&quot; exists in 4 projects as adaptations (avg reusability 52%). Generalize into parameterized template.</p></div>
279
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: button.tsx</strong></div><p>&quot;button.tsx&quot; exists in 8 projects as adaptations (avg reusability 71%). Generalize into parameterized template.</p></div>
280
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: config.py</strong></div><p>&quot;config.py&quot; exists in 5 projects as adaptations (avg reusability 74%). Generalize into parameterized template.</p></div>
281
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: footer.tsx</strong></div><p>&quot;footer.tsx&quot; exists in 8 projects as adaptations (avg reusability 78%). Generalize into parameterized template.</p></div>
282
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: index.ts</strong></div><p>&quot;index.ts&quot; exists in 12 projects as adaptations (avg reusability 63%). Generalize into parameterized template.</p></div>
283
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: input.tsx</strong></div><p>&quot;input.tsx&quot; exists in 7 projects as adaptations (avg reusability 64%). Generalize into parameterized template.</p></div>
284
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: layout.tsx</strong></div><p>&quot;layout.tsx&quot; exists in 12 projects as adaptations (avg reusability 77%). Generalize into parameterized template.</p></div>
285
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: main.py</strong></div><p>&quot;main.py&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
286
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: page.tsx</strong></div><p>&quot;page.tsx&quot; exists in 12 projects as adaptations (avg reusability 77%). Generalize into parameterized template.</p></div>
287
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: postcss.config.js</strong></div><p>&quot;postcss.config.js&quot; exists in 4 projects as adaptations (avg reusability 53%). Generalize into parameterized template.</p></div>
288
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: route.ts</strong></div><p>&quot;route.ts&quot; exists in 11 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
289
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: run.py</strong></div><p>&quot;run.py&quot; exists in 4 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
290
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: sidebar.tsx</strong></div><p>&quot;sidebar.tsx&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
291
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: tailwind.config.js</strong></div><p>&quot;tailwind.config.js&quot; exists in 4 projects as adaptations (avg reusability 56%). Generalize into parameterized template.</p></div>
292
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-debounce.ts</strong></div><p>&quot;use-debounce.ts&quot; exists in 4 projects as adaptations (avg reusability 87%). Generalize into parameterized template.</p></div>
293
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-local-storage.ts</strong></div><p>&quot;use-local-storage.ts&quot; exists in 4 projects as adaptations (avg reusability 74%). Generalize into parameterized template.</p></div>
294
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: use-media-query.ts</strong></div><p>&quot;use-media-query.ts&quot; exists in 4 projects as adaptations (avg reusability 81%). Generalize into parameterized template.</p></div>
295
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: auth.ts</strong></div><p>&quot;auth.ts&quot; exists in 7 projects as adaptations (avg reusability 67%). Generalize into parameterized template.</p></div>
296
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: badge.tsx</strong></div><p>&quot;badge.tsx&quot; exists in 5 projects as adaptations (avg reusability 75%). Generalize into parameterized template.</p></div>
297
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: client.ts</strong></div><p>&quot;client.ts&quot; exists in 6 projects as adaptations (avg reusability 69%). Generalize into parameterized template.</p></div>
298
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: db.ts</strong></div><p>&quot;db.ts&quot; exists in 4 projects as adaptations (avg reusability 73%). Generalize into parameterized template.</p></div>
299
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: error.tsx</strong></div><p>&quot;error.tsx&quot; exists in 4 projects as adaptations (avg reusability 71%). Generalize into parameterized template.</p></div>
300
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: not-found.tsx</strong></div><p>&quot;not-found.tsx&quot; exists in 4 projects as adaptations (avg reusability 76%). Generalize into parameterized template.</p></div>
301
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: select.tsx</strong></div><p>&quot;select.tsx&quot; exists in 5 projects as adaptations (avg reusability 54%). Generalize into parameterized template.</p></div>
302
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: textarea.tsx</strong></div><p>&quot;textarea.tsx&quot; exists in 4 projects as adaptations (avg reusability 63%). Generalize into parameterized template.</p></div>
303
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: utils.ts</strong></div><p>&quot;utils.ts&quot; exists in 7 projects as adaptations (avg reusability 75%). Generalize into parameterized template.</p></div>
304
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: middleware.ts</strong></div><p>&quot;middleware.ts&quot; exists in 5 projects as adaptations (avg reusability 78%). Generalize into parameterized template.</p></div>
305
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: providers.tsx</strong></div><p>&quot;providers.tsx&quot; exists in 5 projects as adaptations (avg reusability 80%). Generalize into parameterized template.</p></div>
306
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: progress.tsx</strong></div><p>&quot;progress.tsx&quot; exists in 4 projects as adaptations (avg reusability 52%). Generalize into parameterized template.</p></div>
307
+ <div class="insight-card orange"><div class="insight-header"><span class="prio prio-75">75</span><strong>Template candidate: tailwind.config.ts</strong></div><p>&quot;tailwind.config.ts&quot; exists in 4 projects as adaptations (avg reusability 88%). Generalize into parameterized template.</p></div></div></div>
308
+ <div class="tab-panel" id="tab-trends"><div class="insight-grid"><p class="empty">Keine Insights in dieser Kategorie.</p></div></div>
309
+ <div class="tab-panel" id="tab-gaps"><div class="insight-grid"><p class="empty">Keine Insights in dieser Kategorie.</p></div></div>
310
+ <div class="tab-panel" id="tab-synergies"><div class="insight-grid"><p class="empty">Keine Insights in dieser Kategorie.</p></div></div>
311
+ <div class="tab-panel" id="tab-warnings"><div class="insight-grid"><p class="empty">Keine Insights in dieser Kategorie.</p></div></div>
312
+ </section>
313
+
314
+ <footer class="reveal reveal-delay-5">
315
+ <p>Brain v2.0.0 &mdash; <code>brain dashboard</code></p>
316
+ </footer>
317
+ </div>
318
+
319
+ <script>
320
+ // --- Neural Network Canvas ---
321
+ (function(){
322
+ const canvas = document.getElementById('neural-bg');
323
+ const ctx = canvas.getContext('2d');
324
+ let W, H, nodes = [], mouse = {x:-1000,y:-1000};
325
+
326
+ function resize(){
327
+ W = canvas.width = window.innerWidth;
328
+ H = canvas.height = window.innerHeight;
329
+ }
330
+ resize();
331
+ window.addEventListener('resize', resize);
332
+ document.addEventListener('mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; });
333
+
334
+ const NODE_COUNT = Math.min(80, Math.floor(window.innerWidth / 18));
335
+ const CONNECT_DIST = 180;
336
+ const MOUSE_DIST = 200;
337
+
338
+ for(let i = 0; i < NODE_COUNT; i++){
339
+ nodes.push({
340
+ x: Math.random() * W,
341
+ y: Math.random() * H,
342
+ vx: (Math.random() - 0.5) * 0.4,
343
+ vy: (Math.random() - 0.5) * 0.4,
344
+ r: Math.random() * 2 + 1,
345
+ pulse: Math.random() * Math.PI * 2,
346
+ });
347
+ }
348
+
349
+ function draw(){
350
+ ctx.clearRect(0, 0, W, H);
351
+
352
+ // Draw connections
353
+ for(let i = 0; i < nodes.length; i++){
354
+ for(let j = i + 1; j < nodes.length; j++){
355
+ const dx = nodes[i].x - nodes[j].x;
356
+ const dy = nodes[i].y - nodes[j].y;
357
+ const dist = Math.sqrt(dx*dx + dy*dy);
358
+ if(dist < CONNECT_DIST){
359
+ const alpha = (1 - dist / CONNECT_DIST) * 0.15;
360
+ ctx.strokeStyle = 'rgba(91,156,255,' + alpha + ')';
361
+ ctx.lineWidth = 0.5;
362
+ ctx.beginPath();
363
+ ctx.moveTo(nodes[i].x, nodes[i].y);
364
+ ctx.lineTo(nodes[j].x, nodes[j].y);
365
+ ctx.stroke();
366
+ }
367
+ }
368
+
369
+ // Mouse interaction
370
+ const mdx = nodes[i].x - mouse.x;
371
+ const mdy = nodes[i].y - mouse.y;
372
+ const mDist = Math.sqrt(mdx*mdx + mdy*mdy);
373
+ if(mDist < MOUSE_DIST){
374
+ const alpha = (1 - mDist / MOUSE_DIST) * 0.4;
375
+ ctx.strokeStyle = 'rgba(180,122,255,' + alpha + ')';
376
+ ctx.lineWidth = 1;
377
+ ctx.beginPath();
378
+ ctx.moveTo(nodes[i].x, nodes[i].y);
379
+ ctx.lineTo(mouse.x, mouse.y);
380
+ ctx.stroke();
381
+ }
382
+ }
383
+
384
+ // Draw nodes
385
+ const time = Date.now() * 0.001;
386
+ for(const n of nodes){
387
+ const glow = 0.4 + Math.sin(time * 1.5 + n.pulse) * 0.3;
388
+ ctx.fillStyle = 'rgba(91,156,255,' + glow + ')';
389
+ ctx.beginPath();
390
+ ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2);
391
+ ctx.fill();
392
+
393
+ n.x += n.vx;
394
+ n.y += n.vy;
395
+ if(n.x < 0 || n.x > W) n.vx *= -1;
396
+ if(n.y < 0 || n.y > H) n.vy *= -1;
397
+ }
398
+
399
+ requestAnimationFrame(draw);
400
+ }
401
+ draw();
402
+ })();
403
+
404
+ // --- Reveal on scroll ---
405
+ const observer = new IntersectionObserver(entries => {
406
+ entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
407
+ }, {threshold: 0.1});
408
+ document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
409
+
410
+ // --- Tab switching ---
411
+ document.querySelectorAll('.tab-btn').forEach(btn => {
412
+ btn.addEventListener('click', () => {
413
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
414
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
415
+ btn.classList.add('active');
416
+ document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
417
+ });
418
+ });
419
+
420
+ // --- Animate stat numbers ---
421
+ const numObserver = new IntersectionObserver(entries => {
422
+ entries.forEach(e => {
423
+ if(!e.isIntersecting) return;
424
+ const el = e.target;
425
+ if(el.dataset.animated) return;
426
+ el.dataset.animated = '1';
427
+ const target = parseInt(el.textContent.replace(/\D/g,''), 10);
428
+ if(isNaN(target) || target === 0) return;
429
+ const duration = 1200;
430
+ const start = performance.now();
431
+ function tick(now){
432
+ const t = Math.min((now - start) / duration, 1);
433
+ const ease = 1 - Math.pow(1 - t, 3);
434
+ el.textContent = Math.round(target * ease).toLocaleString();
435
+ if(t < 1) requestAnimationFrame(tick);
436
+ }
437
+ requestAnimationFrame(tick);
438
+ });
439
+ }, {threshold: 0.5});
440
+ document.querySelectorAll('.stat-number').forEach(el => numObserver.observe(el));
441
+
442
+ // --- Animate language bars ---
443
+ setTimeout(() => {
444
+ document.querySelectorAll('.lang-bar').forEach(bar => {
445
+ bar.style.width = bar.dataset.width + '%';
446
+ });
447
+ }, 300);
448
+
449
+ // --- Activity bar ---
450
+ setTimeout(() => {
451
+ document.querySelectorAll('.activity-fill').forEach(el => {
452
+ el.style.width = el.dataset.target + '%';
453
+ });
454
+ }, 500);
455
+
456
+ // --- Synapse Force-Directed Graph (Premium) ---
457
+ (function(){
458
+ const edges = [{"s":"code_module:16396","t":"project:32","type":"uses_module","w":0.9999999999976071},{"s":"code_module:11809","t":"project:32","type":"uses_module","w":0.9999999997734227},{"s":"code_module:10773","t":"project:32","type":"uses_module","w":0.9999999589029372},{"s":"code_module:10770","t":"project:32","type":"uses_module","w":0.9999998491798466},{"s":"code_module:16403","t":"project:32","type":"uses_module","w":0.9999997110759625},{"s":"code_module:10777","t":"project:32","type":"uses_module","w":0.9999996600893677},{"s":"code_module:10761","t":"project:32","type":"uses_module","w":0.9999925457292852},{"s":"code_module:10764","t":"project:32","type":"uses_module","w":0.9999878619650481},{"s":"code_module:10659","t":"project:32","type":"uses_module","w":0.9999621369230813},{"s":"code_module:11811","t":"project:32","type":"uses_module","w":0.9999621369230813},{"s":"code_module:11906","t":"project:32","type":"uses_module","w":0.9999383463025951},{"s":"code_module:10776","t":"project:32","type":"uses_module","w":0.9999146661627615},{"s":"code_module:10710","t":"project:32","type":"uses_module","w":0.9998818908827148},{"s":"code_module:11808","t":"project:32","type":"uses_module","w":0.9998076790274208},{"s":"code_module:16419","t":"project:32","type":"uses_module","w":0.9997338118026586},{"s":"code_module:16461","t":"project:32","type":"uses_module","w":0.9997338118026586},{"s":"code_module:11806","t":"project:32","type":"uses_module","w":0.9996315734292853},{"s":"code_module:11805","t":"project:32","type":"uses_module","w":0.9994900670301526},{"s":"code_module:11935","t":"project:32","type":"uses_module","w":0.9992942104223566},{"s":"code_module:10772","t":"project:32","type":"uses_module","w":0.9990231286122582},{"s":"code_module:10775","t":"project:32","type":"uses_module","w":0.9988507395438331},{"s":"code_module:399","t":"project:32","type":"uses_module","w":0.9984093280883503},{"s":"code_module:10762","t":"project:32","type":"uses_module","w":0.9974098564434769},{"s":"code_module:11812","t":"project:32","type":"uses_module","w":0.9969527722864434},{"s":"code_module:10657","t":"project:32","type":"uses_module","w":0.995782383787465},{"s":"code_module:399","t":"project:1","type":"uses_module","w":0.9931323163646896},{"s":"code_module:10661","t":"project:32","type":"uses_module","w":0.9931323163646896},{"s":"code_module:10996","t":"project:32","type":"uses_module","w":0.9931323163646896},{"s":"code_module:10995","t":"project:32","type":"uses_module","w":0.9919203721937524},{"s":"code_module:10997","t":"project:32","type":"uses_module","w":0.9919203721937524},{"s":"error:1","t":"project:33","type":"co_occurs","w":0.4472875},{"s":"solution:1","t":"error:1","type":"solves","w":0.34975},{"s":"solution:2","t":"error:2","type":"solves","w":0.34975},{"s":"solution:3","t":"error:3","type":"solves","w":0.34975},{"s":"error:2","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:3","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:4","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:5","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:6","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:7","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:8","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:9","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:10","t":"project:34","type":"co_occurs","w":0.23500000000000001},{"s":"error:11","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:12","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:13","t":"project:33","type":"co_occurs","w":0.23500000000000001},{"s":"error:14","t":"project:36","type":"co_occurs","w":0.23500000000000001},{"s":"code_module:1","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:2","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:3","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:4","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:5","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:6","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:7","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:8","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:9","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:10","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:11","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:12","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:13","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:14","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:15","t":"project:1","type":"co_occurs","w":0.1},{"s":"code_module:16","t":"project:1","type":"co_occurs","w":0.1},{"s":"solution:5","t":"error:5","type":"solves","w":0.1}];
459
+ const canvas = document.getElementById('synapse-graph');
460
+ if (!canvas || !edges.length) return;
461
+ const ctx = canvas.getContext('2d');
462
+ const container = canvas.parentElement;
463
+ let W, H, dpr;
464
+ let frame = 0;
465
+
466
+ const NODE_COLORS = {
467
+ error: '#ff5577', solution: '#3dffa0', code_module: '#b47aff',
468
+ project: '#ffb347', rule: '#5b9cff', antipattern: '#ff5577'
469
+ };
470
+ const EDGE_COLORS = {
471
+ co_occurs: ['#5b9cff','#47e5ff'], solves: ['#3dffa0','#5bff8a'],
472
+ uses_module: ['#b47aff','#7a5cff'], depends_on: ['#ff5577','#ffb347'],
473
+ caused_by: ['#ff5577','#ff8866']
474
+ };
475
+ const DEFAULT_COLOR = '#47e5ff';
476
+
477
+ // Build graph nodes & edges
478
+ const nodeMap = new Map();
479
+ const graphEdges = [];
480
+ for (const e of edges) {
481
+ if (!nodeMap.has(e.s)) nodeMap.set(e.s, { id: e.s, type: e.s.split(':')[0], x: 0, y: 0, vx: 0, vy: 0, connections: 0 });
482
+ if (!nodeMap.has(e.t)) nodeMap.set(e.t, { id: e.t, type: e.t.split(':')[0], x: 0, y: 0, vx: 0, vy: 0, connections: 0 });
483
+ nodeMap.get(e.s).connections++;
484
+ nodeMap.get(e.t).connections++;
485
+ graphEdges.push({ source: nodeMap.get(e.s), target: nodeMap.get(e.t), type: e.type, weight: e.w });
486
+ }
487
+ const nodes = [...nodeMap.values()];
488
+
489
+ function resize() {
490
+ dpr = window.devicePixelRatio || 1;
491
+ W = container.clientWidth;
492
+ H = 650;
493
+ canvas.width = W * dpr;
494
+ canvas.height = H * dpr;
495
+ canvas.style.width = W + 'px';
496
+ canvas.style.height = H + 'px';
497
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
498
+ }
499
+ resize();
500
+ window.addEventListener('resize', resize);
501
+
502
+ // Cluster initial positions by type for better layout
503
+ const typeGroups = {};
504
+ for (const n of nodes) {
505
+ if (!typeGroups[n.type]) typeGroups[n.type] = [];
506
+ typeGroups[n.type].push(n);
507
+ }
508
+ const types = Object.keys(typeGroups);
509
+ types.forEach((t, i) => {
510
+ const angle = (i / types.length) * Math.PI * 2;
511
+ const cx = W/2 + Math.cos(angle) * W * 0.2;
512
+ const cy = H/2 + Math.sin(angle) * H * 0.2;
513
+ for (const n of typeGroups[t]) {
514
+ n.x = cx + (Math.random() - 0.5) * W * 0.25;
515
+ n.y = cy + (Math.random() - 0.5) * H * 0.25;
516
+ }
517
+ });
518
+
519
+ // Force simulation
520
+ const REPULSION = 4000;
521
+ const ATTRACTION = 0.006;
522
+ const DAMPING = 0.88;
523
+ const CENTER_GRAVITY = 0.0015;
524
+ let hovered = null;
525
+ let dragging = null;
526
+ let dragOff = {x:0,y:0};
527
+
528
+ function simulate() {
529
+ for (let i = 0; i < nodes.length; i++) {
530
+ for (let j = i + 1; j < nodes.length; j++) {
531
+ let dx = nodes[i].x - nodes[j].x;
532
+ let dy = nodes[i].y - nodes[j].y;
533
+ let dist = Math.sqrt(dx*dx + dy*dy) || 1;
534
+ let force = REPULSION / (dist * dist);
535
+ let fx = (dx / dist) * force;
536
+ let fy = (dy / dist) * force;
537
+ nodes[i].vx += fx; nodes[i].vy += fy;
538
+ nodes[j].vx -= fx; nodes[j].vy -= fy;
539
+ }
540
+ }
541
+ for (const e of graphEdges) {
542
+ let dx = e.target.x - e.source.x;
543
+ let dy = e.target.y - e.source.y;
544
+ let dist = Math.sqrt(dx*dx + dy*dy) || 1;
545
+ let force = (dist - 120) * ATTRACTION * e.weight;
546
+ let fx = (dx / dist) * force;
547
+ let fy = (dy / dist) * force;
548
+ e.source.vx += fx; e.source.vy += fy;
549
+ e.target.vx -= fx; e.target.vy -= fy;
550
+ }
551
+ for (const n of nodes) {
552
+ n.vx += (W/2 - n.x) * CENTER_GRAVITY;
553
+ n.vy += (H/2 - n.y) * CENTER_GRAVITY;
554
+ }
555
+ for (const n of nodes) {
556
+ if (n === dragging) continue;
557
+ n.vx *= DAMPING; n.vy *= DAMPING;
558
+ n.x += n.vx; n.y += n.vy;
559
+ n.x = Math.max(30, Math.min(W - 30, n.x));
560
+ n.y = Math.max(30, Math.min(H - 30, n.y));
561
+ }
562
+ }
563
+
564
+ function getNodeRadius(n) {
565
+ return Math.min(20, 4 + Math.sqrt(n.connections) * 3.5);
566
+ }
567
+
568
+ function draw() {
569
+ frame++;
570
+ ctx.clearRect(0, 0, W, H);
571
+
572
+ // Edges with gradient colors per type
573
+ for (const e of graphEdges) {
574
+ const alpha = 0.1 + e.weight * 0.4;
575
+ const colors = EDGE_COLORS[e.type] || ['#5b9cff','#47e5ff'];
576
+ const grad = ctx.createLinearGradient(e.source.x, e.source.y, e.target.x, e.target.y);
577
+ grad.addColorStop(0, colors[0] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
578
+ grad.addColorStop(1, colors[1] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
579
+ ctx.strokeStyle = grad;
580
+ ctx.lineWidth = 0.5 + e.weight * 2.5;
581
+ ctx.beginPath();
582
+ ctx.moveTo(e.source.x, e.source.y);
583
+ ctx.lineTo(e.target.x, e.target.y);
584
+ ctx.stroke();
585
+ }
586
+
587
+ // Nodes with ambient glow + pulse on hubs
588
+ for (const n of nodes) {
589
+ const r = getNodeRadius(n);
590
+ const color = NODE_COLORS[n.type] || DEFAULT_COLOR;
591
+ const isHover = n === hovered || n === dragging;
592
+ const isHub = n.connections >= 5;
593
+
594
+ // Ambient glow for all nodes
595
+ const glowSize = isHover ? 30 : (isHub ? 15 + Math.sin(frame * 0.03 + n.x) * 5 : 8);
596
+ ctx.shadowColor = color;
597
+ ctx.shadowBlur = glowSize;
598
+
599
+ // Outer ring for hubs
600
+ if (isHub || isHover) {
601
+ const pulseR = r + 3 + (isHub ? Math.sin(frame * 0.04 + n.y) * 2 : 0);
602
+ ctx.strokeStyle = color;
603
+ ctx.globalAlpha = isHover ? 0.6 : 0.25;
604
+ ctx.lineWidth = 1.5;
605
+ ctx.beginPath();
606
+ ctx.arc(n.x, n.y, pulseR, 0, Math.PI * 2);
607
+ ctx.stroke();
608
+ }
609
+
610
+ // Core node
611
+ ctx.globalAlpha = isHover ? 1 : 0.85;
612
+ ctx.fillStyle = color;
613
+ ctx.beginPath();
614
+ ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
615
+ ctx.fill();
616
+
617
+ // Inner highlight (glossy effect)
618
+ ctx.globalAlpha = 0.3;
619
+ ctx.fillStyle = '#ffffff';
620
+ ctx.beginPath();
621
+ ctx.arc(n.x - r * 0.25, n.y - r * 0.25, r * 0.4, 0, Math.PI * 2);
622
+ ctx.fill();
623
+
624
+ ctx.globalAlpha = 1;
625
+ ctx.shadowBlur = 0;
626
+
627
+ // Labels
628
+ if (isHover || n.connections >= 4) {
629
+ const label = n.type === 'project' ? n.id.replace('project:','P') : n.id.split(':')[0];
630
+ ctx.fillStyle = '#e8eaf6';
631
+ ctx.font = (isHover ? 'bold 12px' : '10px') + ' Inter, system-ui, sans-serif';
632
+ ctx.textAlign = 'center';
633
+ ctx.globalAlpha = isHover ? 1 : 0.7;
634
+ ctx.fillText(isHover ? n.id : label, n.x, n.y - r - 8);
635
+ ctx.globalAlpha = 1;
636
+ }
637
+ }
638
+ simulate();
639
+ requestAnimationFrame(draw);
640
+ }
641
+ draw();
642
+
643
+ // Interaction
644
+ const tooltip = document.getElementById('graph-tooltip');
645
+ function getNodeAt(mx, my) {
646
+ for (let i = nodes.length - 1; i >= 0; i--) {
647
+ const n = nodes[i], r = getNodeRadius(n);
648
+ if (Math.hypot(mx - n.x, my - n.y) <= r + 4) return n;
649
+ }
650
+ return null;
651
+ }
652
+ function getPos(e) {
653
+ const rect = canvas.getBoundingClientRect();
654
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
655
+ }
656
+ canvas.addEventListener('mousemove', function(e) {
657
+ const p = getPos(e);
658
+ if (dragging) {
659
+ dragging.x = p.x + dragOff.x;
660
+ dragging.y = p.y + dragOff.y;
661
+ dragging.vx = 0; dragging.vy = 0;
662
+ return;
663
+ }
664
+ const n = getNodeAt(p.x, p.y);
665
+ hovered = n;
666
+ canvas.style.cursor = n ? 'pointer' : 'grab';
667
+ if (n) {
668
+ const conns = graphEdges.filter(e => e.source === n || e.target === n);
669
+ const types = {};
670
+ conns.forEach(c => { types[c.type] = (types[c.type]||0)+1; });
671
+ const typeStr = Object.entries(types).map(([t,c]) => t+': '+c).join(', ');
672
+ tooltip.innerHTML = '<strong>' + n.id + '</strong><br>' + conns.length + ' connections<br><span style="color:var(--text3);font-size:.75rem">' + typeStr + '</span>';
673
+ tooltip.style.display = 'block';
674
+ tooltip.style.left = (p.x + 15) + 'px';
675
+ tooltip.style.top = (p.y - 10) + 'px';
676
+ } else {
677
+ tooltip.style.display = 'none';
678
+ }
679
+ });
680
+ canvas.addEventListener('mousedown', function(e) {
681
+ const p = getPos(e);
682
+ const n = getNodeAt(p.x, p.y);
683
+ if (n) {
684
+ dragging = n;
685
+ dragOff = { x: n.x - p.x, y: n.y - p.y };
686
+ canvas.style.cursor = 'grabbing';
687
+ }
688
+ });
689
+ canvas.addEventListener('mouseup', function() { dragging = null; });
690
+ canvas.addEventListener('mouseleave', function() { dragging = null; hovered = null; tooltip.style.display = 'none'; });
691
+ })();
692
+ </script>
693
+ </body>
694
+ </html>