@timmeck/marketing-brain 0.2.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 (294) hide show
  1. package/.mcp.json +9 -0
  2. package/README.md +342 -0
  3. package/dashboard.html +666 -0
  4. package/dist/api/server.d.ts +15 -0
  5. package/dist/api/server.js +73 -0
  6. package/dist/api/server.js.map +1 -0
  7. package/dist/cli/colors.d.ts +43 -0
  8. package/dist/cli/colors.js +54 -0
  9. package/dist/cli/colors.js.map +1 -0
  10. package/dist/cli/commands/campaign.d.ts +2 -0
  11. package/dist/cli/commands/campaign.js +62 -0
  12. package/dist/cli/commands/campaign.js.map +1 -0
  13. package/dist/cli/commands/config.d.ts +2 -0
  14. package/dist/cli/commands/config.js +164 -0
  15. package/dist/cli/commands/config.js.map +1 -0
  16. package/dist/cli/commands/dashboard.d.ts +2 -0
  17. package/dist/cli/commands/dashboard.js +147 -0
  18. package/dist/cli/commands/dashboard.js.map +1 -0
  19. package/dist/cli/commands/doctor.d.ts +2 -0
  20. package/dist/cli/commands/doctor.js +111 -0
  21. package/dist/cli/commands/doctor.js.map +1 -0
  22. package/dist/cli/commands/export.d.ts +2 -0
  23. package/dist/cli/commands/export.js +37 -0
  24. package/dist/cli/commands/export.js.map +1 -0
  25. package/dist/cli/commands/import.d.ts +2 -0
  26. package/dist/cli/commands/import.js +76 -0
  27. package/dist/cli/commands/import.js.map +1 -0
  28. package/dist/cli/commands/insights.d.ts +2 -0
  29. package/dist/cli/commands/insights.js +41 -0
  30. package/dist/cli/commands/insights.js.map +1 -0
  31. package/dist/cli/commands/learn.d.ts +2 -0
  32. package/dist/cli/commands/learn.js +22 -0
  33. package/dist/cli/commands/learn.js.map +1 -0
  34. package/dist/cli/commands/network.d.ts +2 -0
  35. package/dist/cli/commands/network.js +66 -0
  36. package/dist/cli/commands/network.js.map +1 -0
  37. package/dist/cli/commands/post.d.ts +2 -0
  38. package/dist/cli/commands/post.js +45 -0
  39. package/dist/cli/commands/post.js.map +1 -0
  40. package/dist/cli/commands/query.d.ts +2 -0
  41. package/dist/cli/commands/query.js +96 -0
  42. package/dist/cli/commands/query.js.map +1 -0
  43. package/dist/cli/commands/rules.d.ts +2 -0
  44. package/dist/cli/commands/rules.js +25 -0
  45. package/dist/cli/commands/rules.js.map +1 -0
  46. package/dist/cli/commands/start.d.ts +2 -0
  47. package/dist/cli/commands/start.js +91 -0
  48. package/dist/cli/commands/start.js.map +1 -0
  49. package/dist/cli/commands/status.d.ts +2 -0
  50. package/dist/cli/commands/status.js +63 -0
  51. package/dist/cli/commands/status.js.map +1 -0
  52. package/dist/cli/commands/stop.d.ts +2 -0
  53. package/dist/cli/commands/stop.js +34 -0
  54. package/dist/cli/commands/stop.js.map +1 -0
  55. package/dist/cli/commands/suggest.d.ts +2 -0
  56. package/dist/cli/commands/suggest.js +57 -0
  57. package/dist/cli/commands/suggest.js.map +1 -0
  58. package/dist/cli/ipc-helper.d.ts +2 -0
  59. package/dist/cli/ipc-helper.js +26 -0
  60. package/dist/cli/ipc-helper.js.map +1 -0
  61. package/dist/cli/update-check.d.ts +2 -0
  62. package/dist/cli/update-check.js +58 -0
  63. package/dist/cli/update-check.js.map +1 -0
  64. package/dist/config.d.ts +2 -0
  65. package/dist/config.js +111 -0
  66. package/dist/config.js.map +1 -0
  67. package/dist/dashboard/renderer.d.ts +11 -0
  68. package/dist/dashboard/renderer.js +112 -0
  69. package/dist/dashboard/renderer.js.map +1 -0
  70. package/dist/dashboard/server.d.ts +15 -0
  71. package/dist/dashboard/server.js +122 -0
  72. package/dist/dashboard/server.js.map +1 -0
  73. package/dist/db/connection.d.ts +2 -0
  74. package/dist/db/connection.js +19 -0
  75. package/dist/db/connection.js.map +1 -0
  76. package/dist/db/migrations/001_core_schema.d.ts +2 -0
  77. package/dist/db/migrations/001_core_schema.js +62 -0
  78. package/dist/db/migrations/001_core_schema.js.map +1 -0
  79. package/dist/db/migrations/002_learning_schema.d.ts +2 -0
  80. package/dist/db/migrations/002_learning_schema.js +45 -0
  81. package/dist/db/migrations/002_learning_schema.js.map +1 -0
  82. package/dist/db/migrations/003_synapse_schema.d.ts +2 -0
  83. package/dist/db/migrations/003_synapse_schema.js +26 -0
  84. package/dist/db/migrations/003_synapse_schema.js.map +1 -0
  85. package/dist/db/migrations/004_insights_schema.d.ts +2 -0
  86. package/dist/db/migrations/004_insights_schema.js +37 -0
  87. package/dist/db/migrations/004_insights_schema.js.map +1 -0
  88. package/dist/db/migrations/005_fts_indexes.d.ts +2 -0
  89. package/dist/db/migrations/005_fts_indexes.js +76 -0
  90. package/dist/db/migrations/005_fts_indexes.js.map +1 -0
  91. package/dist/db/migrations/index.d.ts +2 -0
  92. package/dist/db/migrations/index.js +47 -0
  93. package/dist/db/migrations/index.js.map +1 -0
  94. package/dist/db/repositories/audience.repository.d.ts +18 -0
  95. package/dist/db/repositories/audience.repository.js +45 -0
  96. package/dist/db/repositories/audience.repository.js.map +1 -0
  97. package/dist/db/repositories/campaign.repository.d.ts +15 -0
  98. package/dist/db/repositories/campaign.repository.js +58 -0
  99. package/dist/db/repositories/campaign.repository.js.map +1 -0
  100. package/dist/db/repositories/engagement.repository.d.ts +26 -0
  101. package/dist/db/repositories/engagement.repository.js +83 -0
  102. package/dist/db/repositories/engagement.repository.js.map +1 -0
  103. package/dist/db/repositories/insight.repository.d.ts +18 -0
  104. package/dist/db/repositories/insight.repository.js +87 -0
  105. package/dist/db/repositories/insight.repository.js.map +1 -0
  106. package/dist/db/repositories/post.repository.d.ts +21 -0
  107. package/dist/db/repositories/post.repository.js +105 -0
  108. package/dist/db/repositories/post.repository.js.map +1 -0
  109. package/dist/db/repositories/rule.repository.d.ts +16 -0
  110. package/dist/db/repositories/rule.repository.js +71 -0
  111. package/dist/db/repositories/rule.repository.js.map +1 -0
  112. package/dist/db/repositories/strategy.repository.d.ts +16 -0
  113. package/dist/db/repositories/strategy.repository.js +69 -0
  114. package/dist/db/repositories/strategy.repository.js.map +1 -0
  115. package/dist/db/repositories/synapse.repository.d.ts +25 -0
  116. package/dist/db/repositories/synapse.repository.js +115 -0
  117. package/dist/db/repositories/synapse.repository.js.map +1 -0
  118. package/dist/db/repositories/template.repository.d.ts +16 -0
  119. package/dist/db/repositories/template.repository.js +61 -0
  120. package/dist/db/repositories/template.repository.js.map +1 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.js +62 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/ipc/client.d.ts +13 -0
  125. package/dist/ipc/client.js +93 -0
  126. package/dist/ipc/client.js.map +1 -0
  127. package/dist/ipc/protocol.d.ts +8 -0
  128. package/dist/ipc/protocol.js +29 -0
  129. package/dist/ipc/protocol.js.map +1 -0
  130. package/dist/ipc/router.d.ts +30 -0
  131. package/dist/ipc/router.js +88 -0
  132. package/dist/ipc/router.js.map +1 -0
  133. package/dist/ipc/server.d.ts +14 -0
  134. package/dist/ipc/server.js +130 -0
  135. package/dist/ipc/server.js.map +1 -0
  136. package/dist/learning/confidence-scorer.d.ts +17 -0
  137. package/dist/learning/confidence-scorer.js +26 -0
  138. package/dist/learning/confidence-scorer.js.map +1 -0
  139. package/dist/learning/learning-engine.d.ts +33 -0
  140. package/dist/learning/learning-engine.js +211 -0
  141. package/dist/learning/learning-engine.js.map +1 -0
  142. package/dist/marketing-core.d.ts +17 -0
  143. package/dist/marketing-core.js +233 -0
  144. package/dist/marketing-core.js.map +1 -0
  145. package/dist/mcp/server.d.ts +1 -0
  146. package/dist/mcp/server.js +67 -0
  147. package/dist/mcp/server.js.map +1 -0
  148. package/dist/mcp/tools.d.ts +3 -0
  149. package/dist/mcp/tools.js +138 -0
  150. package/dist/mcp/tools.js.map +1 -0
  151. package/dist/research/research-engine.d.ts +28 -0
  152. package/dist/research/research-engine.js +211 -0
  153. package/dist/research/research-engine.js.map +1 -0
  154. package/dist/services/analytics.service.d.ts +116 -0
  155. package/dist/services/analytics.service.js +69 -0
  156. package/dist/services/analytics.service.js.map +1 -0
  157. package/dist/services/audience.service.d.ts +20 -0
  158. package/dist/services/audience.service.js +30 -0
  159. package/dist/services/audience.service.js.map +1 -0
  160. package/dist/services/campaign.service.d.ts +27 -0
  161. package/dist/services/campaign.service.js +65 -0
  162. package/dist/services/campaign.service.js.map +1 -0
  163. package/dist/services/insight.service.d.ts +18 -0
  164. package/dist/services/insight.service.js +40 -0
  165. package/dist/services/insight.service.js.map +1 -0
  166. package/dist/services/post.service.d.ts +48 -0
  167. package/dist/services/post.service.js +93 -0
  168. package/dist/services/post.service.js.map +1 -0
  169. package/dist/services/rule.service.d.ts +29 -0
  170. package/dist/services/rule.service.js +67 -0
  171. package/dist/services/rule.service.js.map +1 -0
  172. package/dist/services/strategy.service.d.ts +17 -0
  173. package/dist/services/strategy.service.js +39 -0
  174. package/dist/services/strategy.service.js.map +1 -0
  175. package/dist/services/synapse.service.d.ts +22 -0
  176. package/dist/services/synapse.service.js +22 -0
  177. package/dist/services/synapse.service.js.map +1 -0
  178. package/dist/services/template.service.d.ts +17 -0
  179. package/dist/services/template.service.js +37 -0
  180. package/dist/services/template.service.js.map +1 -0
  181. package/dist/synapses/activation.d.ts +13 -0
  182. package/dist/synapses/activation.js +50 -0
  183. package/dist/synapses/activation.js.map +1 -0
  184. package/dist/synapses/decay.d.ts +11 -0
  185. package/dist/synapses/decay.js +27 -0
  186. package/dist/synapses/decay.js.map +1 -0
  187. package/dist/synapses/hebbian.d.ts +13 -0
  188. package/dist/synapses/hebbian.js +35 -0
  189. package/dist/synapses/hebbian.js.map +1 -0
  190. package/dist/synapses/pathfinder.d.ts +14 -0
  191. package/dist/synapses/pathfinder.js +50 -0
  192. package/dist/synapses/pathfinder.js.map +1 -0
  193. package/dist/synapses/synapse-manager.d.ts +32 -0
  194. package/dist/synapses/synapse-manager.js +76 -0
  195. package/dist/synapses/synapse-manager.js.map +1 -0
  196. package/dist/types/config.types.d.ts +69 -0
  197. package/dist/types/config.types.js +2 -0
  198. package/dist/types/config.types.js.map +1 -0
  199. package/dist/types/ipc.types.d.ts +11 -0
  200. package/dist/types/ipc.types.js +2 -0
  201. package/dist/types/ipc.types.js.map +1 -0
  202. package/dist/types/post.types.d.ts +141 -0
  203. package/dist/types/post.types.js +2 -0
  204. package/dist/types/post.types.js.map +1 -0
  205. package/dist/types/synapse.types.d.ts +23 -0
  206. package/dist/types/synapse.types.js +2 -0
  207. package/dist/types/synapse.types.js.map +1 -0
  208. package/dist/utils/events.d.ts +57 -0
  209. package/dist/utils/events.js +23 -0
  210. package/dist/utils/events.js.map +1 -0
  211. package/dist/utils/hash.d.ts +1 -0
  212. package/dist/utils/hash.js +5 -0
  213. package/dist/utils/hash.js.map +1 -0
  214. package/dist/utils/logger.d.ts +8 -0
  215. package/dist/utils/logger.js +39 -0
  216. package/dist/utils/logger.js.map +1 -0
  217. package/dist/utils/paths.d.ts +3 -0
  218. package/dist/utils/paths.js +18 -0
  219. package/dist/utils/paths.js.map +1 -0
  220. package/package.json +40 -0
  221. package/seed-data.json +78 -0
  222. package/src/api/server.ts +86 -0
  223. package/src/cli/colors.ts +59 -0
  224. package/src/cli/commands/campaign.ts +66 -0
  225. package/src/cli/commands/config.ts +168 -0
  226. package/src/cli/commands/dashboard.ts +165 -0
  227. package/src/cli/commands/doctor.ts +110 -0
  228. package/src/cli/commands/export.ts +40 -0
  229. package/src/cli/commands/import.ts +84 -0
  230. package/src/cli/commands/insights.ts +44 -0
  231. package/src/cli/commands/learn.ts +24 -0
  232. package/src/cli/commands/network.ts +71 -0
  233. package/src/cli/commands/post.ts +47 -0
  234. package/src/cli/commands/query.ts +108 -0
  235. package/src/cli/commands/rules.ts +27 -0
  236. package/src/cli/commands/start.ts +100 -0
  237. package/src/cli/commands/status.ts +73 -0
  238. package/src/cli/commands/stop.ts +33 -0
  239. package/src/cli/commands/suggest.ts +64 -0
  240. package/src/cli/ipc-helper.ts +22 -0
  241. package/src/cli/update-check.ts +63 -0
  242. package/src/config.ts +110 -0
  243. package/src/dashboard/renderer.ts +136 -0
  244. package/src/dashboard/server.ts +140 -0
  245. package/src/db/connection.ts +22 -0
  246. package/src/db/migrations/001_core_schema.ts +63 -0
  247. package/src/db/migrations/002_learning_schema.ts +46 -0
  248. package/src/db/migrations/003_synapse_schema.ts +27 -0
  249. package/src/db/migrations/004_insights_schema.ts +38 -0
  250. package/src/db/migrations/005_fts_indexes.ts +77 -0
  251. package/src/db/migrations/index.ts +62 -0
  252. package/src/db/repositories/audience.repository.ts +53 -0
  253. package/src/db/repositories/campaign.repository.ts +72 -0
  254. package/src/db/repositories/engagement.repository.ts +108 -0
  255. package/src/db/repositories/insight.repository.ts +100 -0
  256. package/src/db/repositories/post.repository.ts +123 -0
  257. package/src/db/repositories/rule.repository.ts +87 -0
  258. package/src/db/repositories/strategy.repository.ts +82 -0
  259. package/src/db/repositories/synapse.repository.ts +148 -0
  260. package/src/db/repositories/template.repository.ts +76 -0
  261. package/src/index.ts +69 -0
  262. package/src/ipc/client.ts +110 -0
  263. package/src/ipc/protocol.ts +35 -0
  264. package/src/ipc/router.ts +126 -0
  265. package/src/ipc/server.ts +140 -0
  266. package/src/learning/confidence-scorer.ts +36 -0
  267. package/src/learning/learning-engine.ts +254 -0
  268. package/src/marketing-core.ts +285 -0
  269. package/src/mcp/server.ts +72 -0
  270. package/src/mcp/tools.ts +216 -0
  271. package/src/research/research-engine.ts +226 -0
  272. package/src/services/analytics.service.ts +73 -0
  273. package/src/services/audience.service.ts +40 -0
  274. package/src/services/campaign.service.ts +80 -0
  275. package/src/services/insight.service.ts +54 -0
  276. package/src/services/post.service.ts +116 -0
  277. package/src/services/rule.service.ts +90 -0
  278. package/src/services/strategy.service.ts +53 -0
  279. package/src/services/synapse.service.ts +32 -0
  280. package/src/services/template.service.ts +50 -0
  281. package/src/synapses/activation.ts +80 -0
  282. package/src/synapses/decay.ts +38 -0
  283. package/src/synapses/hebbian.ts +68 -0
  284. package/src/synapses/pathfinder.ts +81 -0
  285. package/src/synapses/synapse-manager.ts +115 -0
  286. package/src/types/config.types.ts +79 -0
  287. package/src/types/ipc.types.ts +8 -0
  288. package/src/types/post.types.ts +156 -0
  289. package/src/types/synapse.types.ts +43 -0
  290. package/src/utils/events.ts +44 -0
  291. package/src/utils/hash.ts +5 -0
  292. package/src/utils/logger.ts +48 -0
  293. package/src/utils/paths.ts +19 -0
  294. package/tsconfig.json +18 -0
package/dashboard.html ADDED
@@ -0,0 +1,666 @@
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>Marketing 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
+ /* Platform chart */
111
+ .platform-chart{max-width:650px}
112
+ .platform-row{display:flex;align-items:center;gap:14px;margin-bottom:10px}
113
+ .platform-name{width:100px;text-align:right;font-size:.85rem;color:var(--text2);font-weight:500}
114
+ .platform-bar-bg{flex:1;height:28px;background:var(--bg3);border-radius:6px;overflow:hidden;border:1px solid var(--glass-border)}
115
+ .platform-bar{height:100%;border-radius:6px;width:0;transition:width 1.2s cubic-bezier(.22,1,.36,1)}
116
+ .platform-bar.x-bar{background:linear-gradient(90deg,#1da1f2,#47e5ff)}
117
+ .platform-bar.reddit-bar{background:linear-gradient(90deg,#ff4500,#ffb347)}
118
+ .platform-bar.linkedin-bar{background:linear-gradient(90deg,#0077b5,#5b9cff)}
119
+ .platform-bar.bluesky-bar{background:linear-gradient(90deg,#0085ff,#b47aff)}
120
+ .platform-bar.default-bar{background:var(--accent)}
121
+ .platform-count{width:50px;font-size:.85rem;color:var(--text2);font-weight:600}
122
+
123
+ /* Top posts */
124
+ .post-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(380px,1fr));gap:14px}
125
+ .post-card{
126
+ background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);
127
+ padding:18px;border-left:3px solid var(--text3);transition:all .25s ease;backdrop-filter:blur(20px);
128
+ }
129
+ .post-card:hover{transform:translateX(6px);border-color:var(--glass-hover);box-shadow:0 8px 30px rgba(0,0,0,.2)}
130
+ .post-card.x{border-left-color:#1da1f2}.post-card.reddit{border-left-color:#ff4500}
131
+ .post-card.linkedin{border-left-color:#0077b5}.post-card.bluesky{border-left-color:#0085ff}
132
+ .post-meta{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
133
+ .post-platform{font-size:.72rem;padding:3px 10px;border-radius:12px;text-transform:uppercase;font-weight:700;letter-spacing:.5px}
134
+ .post-platform.x{background:rgba(29,161,242,.15);color:#1da1f2;border:1px solid rgba(29,161,242,.25)}
135
+ .post-platform.reddit{background:rgba(255,69,0,.15);color:#ff4500;border:1px solid rgba(255,69,0,.25)}
136
+ .post-platform.linkedin{background:rgba(0,119,181,.15);color:#0077b5;border:1px solid rgba(0,119,181,.25)}
137
+ .post-platform.bluesky{background:rgba(0,133,255,.15);color:#0085ff;border:1px solid rgba(0,133,255,.25)}
138
+ .post-card p{color:var(--text2);font-size:.85rem;line-height:1.5}
139
+ .post-engagement{display:flex;gap:16px;margin-top:10px;font-size:.78rem;color:var(--text3)}
140
+ .post-engagement span{display:flex;align-items:center;gap:4px}
141
+
142
+ /* Insight tabs */
143
+ .tab-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:24px}
144
+ .tab-btn{
145
+ padding:10px 20px;border-radius:24px;border:1px solid var(--glass-border);
146
+ background:var(--glass);color:var(--text2);cursor:pointer;font-size:.85rem;font-weight:500;
147
+ transition:all .3s ease;backdrop-filter:blur(10px);font-family:inherit;
148
+ }
149
+ .tab-btn:hover{border-color:var(--glass-hover);color:var(--text);transform:translateY(-1px)}
150
+ .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)}
151
+ .tab-btn .count{background:var(--bg4);padding:2px 8px;border-radius:12px;font-size:.72rem;margin-left:6px;font-weight:600}
152
+ .tab-panel{display:none}.tab-panel.active{display:block}
153
+ .insight-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(380px,1fr));gap:14px}
154
+ .insight-card{
155
+ background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);
156
+ padding:18px;border-left:3px solid var(--text3);transition:all .25s ease;backdrop-filter:blur(20px);
157
+ }
158
+ .insight-card:hover{transform:translateX(6px);border-color:var(--glass-hover);box-shadow:0 8px 30px rgba(0,0,0,.2)}
159
+ .insight-card.cyan{border-left-color:var(--cyan)}.insight-card.orange{border-left-color:var(--orange)}
160
+ .insight-card.green{border-left-color:var(--green)}.insight-card.red{border-left-color:var(--red)}
161
+ .insight-card.purple{border-left-color:var(--purple)}.insight-card.blue{border-left-color:var(--blue)}
162
+ .insight-header{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
163
+ .insight-card p{color:var(--text2);font-size:.85rem;line-height:1.5}
164
+ .prio{font-size:.68rem;padding:3px 10px;border-radius:12px;text-transform:uppercase;font-weight:700;letter-spacing:.5px}
165
+ .prio-high{background:rgba(255,179,71,.15);color:var(--orange);border:1px solid rgba(255,179,71,.25)}
166
+ .prio-medium{background:rgba(91,156,255,.15);color:var(--blue);border:1px solid rgba(91,156,255,.25)}
167
+ .prio-low{background:rgba(139,143,176,.1);color:var(--text2);border:1px solid rgba(139,143,176,.2)}
168
+ .empty{color:var(--text3);font-style:italic;padding:24px}
169
+
170
+ /* Synapse graph */
171
+ .graph-container{position:relative;background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius);overflow:hidden;backdrop-filter:blur(20px)}
172
+ #synapse-graph{width:100%;height:650px;display:block;cursor:grab}
173
+ #synapse-graph:active{cursor:grabbing}
174
+ .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)}
175
+ .legend-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:6px;vertical-align:middle}
176
+ .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)}
177
+
178
+ /* Rules section */
179
+ .rules-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px}
180
+ .rule-card{
181
+ background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);
182
+ padding:16px;transition:all .25s ease;backdrop-filter:blur(20px);
183
+ }
184
+ .rule-card:hover{transform:translateX(4px);border-color:var(--glass-hover)}
185
+ .rule-pattern{font-size:.75rem;color:var(--cyan);font-weight:600;margin-bottom:6px;font-family:'Fira Code',monospace}
186
+ .rule-recommendation{color:var(--text2);font-size:.85rem}
187
+ .rule-confidence{display:inline-flex;align-items:center;gap:6px;margin-top:8px;font-size:.75rem;color:var(--text3)}
188
+ .confidence-bar{width:60px;height:4px;border-radius:2px;background:var(--bg4);overflow:hidden}
189
+ .confidence-fill{height:100%;border-radius:2px;background:var(--green)}
190
+
191
+ /* Footer */
192
+ footer{text-align:center;padding:40px 0;border-top:1px solid var(--glass-border)}
193
+ footer p{color:var(--text3);font-size:.8rem}
194
+ footer code{background:var(--glass);padding:3px 10px;border-radius:6px;font-size:.78rem;border:1px solid var(--glass-border)}
195
+
196
+ /* Responsive */
197
+ @media(max-width:600px){.stats-grid{grid-template-columns:1fr 1fr}.insight-grid{grid-template-columns:1fr}.post-list{grid-template-columns:1fr}.logo h1{font-size:2rem}}
198
+ </style>
199
+ </head>
200
+ <body>
201
+
202
+ <canvas id="neural-bg"></canvas>
203
+ <div class="orb orb-1"></div>
204
+ <div class="orb orb-2"></div>
205
+ <div class="orb orb-3"></div>
206
+
207
+ <div class="container">
208
+ <header class="reveal">
209
+ <div class="logo">
210
+ <div class="logo-icon">&#128227;</div>
211
+ <h1>Marketing Brain</h1>
212
+ </div>
213
+ <p class="tagline">Self-Learning Marketing Intelligence</p>
214
+ <div class="activity">
215
+ <span class="activity-dot"></span>
216
+ <span class="activity-text">Marketing Intelligence</span>
217
+ <div class="activity-bar"><div class="activity-fill" style="width:0%" data-target="{{ACTIVITY}}"></div></div>
218
+ <span class="activity-text" style="color:var(--cyan);font-weight:700">{{ACTIVITY}}%</span>
219
+ </div>
220
+ </header>
221
+
222
+ <nav class="reveal reveal-delay-1">
223
+ <a href="#stats">Stats</a>
224
+ <a href="#platforms">Platforms</a>
225
+ <a href="#top-posts">Top Posts</a>
226
+ <a href="#network">&#128300; Network</a>
227
+ <a href="#rules">Rules</a>
228
+ <a href="#research" class="research">&#128161; Research</a>
229
+ </nav>
230
+
231
+ <section id="stats" class="reveal reveal-delay-2">
232
+ <div class="section-title"><div class="icon" style="background:rgba(91,156,255,.1)">&#128202;</div> Marketing Status</div>
233
+ <div class="stats-grid">
234
+ <div class="stat-card blue"><div class="stat-number">{{POSTS}}</div><div class="stat-label">Posts</div></div>
235
+ <div class="stat-card purple"><div class="stat-number">{{CAMPAIGNS}}</div><div class="stat-label">Campaigns</div></div>
236
+ <div class="stat-card cyan"><div class="stat-number">{{STRATEGIES}}</div><div class="stat-label">Strategies</div></div>
237
+ <div class="stat-card orange"><div class="stat-number">{{RULES}}</div><div class="stat-label">Rules</div></div>
238
+ <div class="stat-card green"><div class="stat-number">{{TEMPLATES}}</div><div class="stat-label">Templates</div></div>
239
+ <div class="stat-card red"><div class="stat-number">{{SYNAPSES}}</div><div class="stat-label">Synapses</div></div>
240
+ </div>
241
+ </section>
242
+
243
+ <section id="platforms" class="reveal reveal-delay-3">
244
+ <div class="section-title"><div class="icon" style="background:rgba(180,122,255,.1)">&#127760;</div> Platform Distribution</div>
245
+ <div class="platform-chart">{{PLATFORM_CHART}}</div>
246
+ </section>
247
+
248
+ <section id="top-posts" class="reveal reveal-delay-4">
249
+ <div class="section-title"><div class="icon" style="background:rgba(61,255,160,.1)">&#128293;</div> Top Performing Posts</div>
250
+ <div class="post-list">{{TOP_POSTS}}</div>
251
+ </section>
252
+
253
+ <section id="network" class="reveal reveal-delay-4">
254
+ <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Synapse Network</div>
255
+ <div class="graph-container">
256
+ <canvas id="synapse-graph"></canvas>
257
+ <div class="graph-legend">
258
+ <span><span class="legend-dot" style="background:#5b9cff"></span> post</span>
259
+ <span><span class="legend-dot" style="background:#b47aff"></span> campaign</span>
260
+ <span><span class="legend-dot" style="background:#3dffa0"></span> strategy</span>
261
+ <span><span class="legend-dot" style="background:#ffb347"></span> template</span>
262
+ <span><span class="legend-dot" style="background:#ff5577"></span> rule</span>
263
+ <span><span class="legend-dot" style="background:#47e5ff"></span> insight</span>
264
+ <span style="margin-left:auto;color:var(--text3);font-size:.72rem">edges: belongs_to / similar_to / improves / generated_from</span>
265
+ </div>
266
+ <div id="graph-tooltip" class="graph-tooltip"></div>
267
+ </div>
268
+ </section>
269
+
270
+ <section id="rules" class="reveal reveal-delay-5">
271
+ <div class="section-title"><div class="icon" style="background:rgba(255,179,71,.1)">&#128207;</div> Learned Rules</div>
272
+ <div class="rules-list">{{RULES_LIST}}</div>
273
+ </section>
274
+
275
+ <section id="research" class="reveal reveal-delay-5">
276
+ <div class="section-title"><div class="icon" style="background:rgba(71,229,255,.1)">&#128300;</div> Research Insights</div>
277
+ <div class="tab-bar">
278
+ <button class="tab-btn active" data-tab="trends">&#128200; Trends <span class="count">{{TRENDS_COUNT}}</span></button>
279
+ <button class="tab-btn" data-tab="gaps">&#9888;&#65039; Gaps <span class="count">{{GAPS_COUNT}}</span></button>
280
+ <button class="tab-btn" data-tab="synergies">&#9889; Synergies <span class="count">{{SYNERGIES_COUNT}}</span></button>
281
+ <button class="tab-btn" data-tab="templates">&#127912; Templates <span class="count">{{TEMPLATES_COUNT}}</span></button>
282
+ <button class="tab-btn" data-tab="optimizations">&#128161; Optimizations <span class="count">{{OPTIMIZATIONS_COUNT}}</span></button>
283
+ </div>
284
+ <div class="tab-panel active" id="tab-trends"><div class="insight-grid">{{TRENDS}}</div></div>
285
+ <div class="tab-panel" id="tab-gaps"><div class="insight-grid">{{GAPS}}</div></div>
286
+ <div class="tab-panel" id="tab-synergies"><div class="insight-grid">{{SYNERGIES}}</div></div>
287
+ <div class="tab-panel" id="tab-templates"><div class="insight-grid">{{TEMPLATES_INSIGHTS}}</div></div>
288
+ <div class="tab-panel" id="tab-optimizations"><div class="insight-grid">{{OPTIMIZATIONS}}</div></div>
289
+ </section>
290
+
291
+ <footer class="reveal reveal-delay-5">
292
+ <p>Marketing Brain v{{VERSION}} &mdash; <code>marketing dashboard</code></p>
293
+ </footer>
294
+ </div>
295
+
296
+ <script>
297
+ // --- Neural Network Canvas ---
298
+ (function(){
299
+ const canvas = document.getElementById('neural-bg');
300
+ const ctx = canvas.getContext('2d');
301
+ let W, H, nodes = [], mouse = {x:-1000,y:-1000};
302
+
303
+ function resize(){
304
+ W = canvas.width = window.innerWidth;
305
+ H = canvas.height = window.innerHeight;
306
+ }
307
+ resize();
308
+ window.addEventListener('resize', resize);
309
+ document.addEventListener('mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; });
310
+
311
+ const NODE_COUNT = Math.min(80, Math.floor(window.innerWidth / 18));
312
+ const CONNECT_DIST = 180;
313
+ const MOUSE_DIST = 200;
314
+
315
+ for(let i = 0; i < NODE_COUNT; i++){
316
+ nodes.push({
317
+ x: Math.random() * W,
318
+ y: Math.random() * H,
319
+ vx: (Math.random() - 0.5) * 0.4,
320
+ vy: (Math.random() - 0.5) * 0.4,
321
+ r: Math.random() * 2 + 1,
322
+ pulse: Math.random() * Math.PI * 2,
323
+ });
324
+ }
325
+
326
+ function draw(){
327
+ ctx.clearRect(0, 0, W, H);
328
+
329
+ for(let i = 0; i < nodes.length; i++){
330
+ for(let j = i + 1; j < nodes.length; j++){
331
+ const dx = nodes[i].x - nodes[j].x;
332
+ const dy = nodes[i].y - nodes[j].y;
333
+ const dist = Math.sqrt(dx*dx + dy*dy);
334
+ if(dist < CONNECT_DIST){
335
+ const alpha = (1 - dist / CONNECT_DIST) * 0.15;
336
+ ctx.strokeStyle = 'rgba(91,156,255,' + alpha + ')';
337
+ ctx.lineWidth = 0.5;
338
+ ctx.beginPath();
339
+ ctx.moveTo(nodes[i].x, nodes[i].y);
340
+ ctx.lineTo(nodes[j].x, nodes[j].y);
341
+ ctx.stroke();
342
+ }
343
+ }
344
+
345
+ const mdx = nodes[i].x - mouse.x;
346
+ const mdy = nodes[i].y - mouse.y;
347
+ const mDist = Math.sqrt(mdx*mdx + mdy*mdy);
348
+ if(mDist < MOUSE_DIST){
349
+ const alpha = (1 - mDist / MOUSE_DIST) * 0.4;
350
+ ctx.strokeStyle = 'rgba(180,122,255,' + alpha + ')';
351
+ ctx.lineWidth = 1;
352
+ ctx.beginPath();
353
+ ctx.moveTo(nodes[i].x, nodes[i].y);
354
+ ctx.lineTo(mouse.x, mouse.y);
355
+ ctx.stroke();
356
+ }
357
+ }
358
+
359
+ const time = Date.now() * 0.001;
360
+ for(const n of nodes){
361
+ const glow = 0.4 + Math.sin(time * 1.5 + n.pulse) * 0.3;
362
+ ctx.fillStyle = 'rgba(91,156,255,' + glow + ')';
363
+ ctx.beginPath();
364
+ ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2);
365
+ ctx.fill();
366
+
367
+ n.x += n.vx;
368
+ n.y += n.vy;
369
+ if(n.x < 0 || n.x > W) n.vx *= -1;
370
+ if(n.y < 0 || n.y > H) n.vy *= -1;
371
+ }
372
+
373
+ requestAnimationFrame(draw);
374
+ }
375
+ draw();
376
+ })();
377
+
378
+ // --- Reveal on scroll ---
379
+ const observer = new IntersectionObserver(entries => {
380
+ entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
381
+ }, {threshold: 0.1});
382
+ document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
383
+
384
+ // --- Tab switching ---
385
+ document.querySelectorAll('.tab-btn').forEach(btn => {
386
+ btn.addEventListener('click', () => {
387
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
388
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
389
+ btn.classList.add('active');
390
+ document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
391
+ });
392
+ });
393
+
394
+ // --- Animate stat numbers ---
395
+ const numObserver = new IntersectionObserver(entries => {
396
+ entries.forEach(e => {
397
+ if(!e.isIntersecting) return;
398
+ const el = e.target;
399
+ if(el.dataset.animated) return;
400
+ el.dataset.animated = '1';
401
+ const target = parseInt(el.textContent.replace(/\D/g,''), 10);
402
+ if(isNaN(target) || target === 0) return;
403
+ const duration = 1200;
404
+ const start = performance.now();
405
+ function tick(now){
406
+ const t = Math.min((now - start) / duration, 1);
407
+ const ease = 1 - Math.pow(1 - t, 3);
408
+ el.textContent = Math.round(target * ease).toLocaleString();
409
+ if(t < 1) requestAnimationFrame(tick);
410
+ }
411
+ requestAnimationFrame(tick);
412
+ });
413
+ }, {threshold: 0.5});
414
+ document.querySelectorAll('.stat-number').forEach(el => numObserver.observe(el));
415
+
416
+ // --- Animate platform bars ---
417
+ setTimeout(() => {
418
+ document.querySelectorAll('.platform-bar').forEach(bar => {
419
+ bar.style.width = bar.dataset.width + '%';
420
+ });
421
+ }, 300);
422
+
423
+ // --- Activity bar ---
424
+ setTimeout(() => {
425
+ document.querySelectorAll('.activity-fill').forEach(el => {
426
+ el.style.width = el.dataset.target + '%';
427
+ });
428
+ }, 500);
429
+
430
+ // --- Confidence bars ---
431
+ setTimeout(() => {
432
+ document.querySelectorAll('.confidence-fill').forEach(el => {
433
+ el.style.width = el.dataset.width + '%';
434
+ });
435
+ }, 600);
436
+
437
+ // --- Synapse Force-Directed Graph ---
438
+ (function(){
439
+ const edges = {{GRAPH_EDGES}};
440
+ const canvas = document.getElementById('synapse-graph');
441
+ if (!canvas || !edges.length) return;
442
+ const ctx = canvas.getContext('2d');
443
+ const container = canvas.parentElement;
444
+ let W, H, dpr;
445
+ let frame = 0;
446
+
447
+ const NODE_COLORS = {
448
+ post: '#5b9cff', campaign: '#b47aff', strategy: '#3dffa0',
449
+ template: '#ffb347', rule: '#ff5577', insight: '#47e5ff', audience: '#b47aff'
450
+ };
451
+ const EDGE_COLORS = {
452
+ belongs_to: ['#b47aff','#5b9cff'], similar_to: ['#5b9cff','#47e5ff'],
453
+ improves: ['#3dffa0','#5bff8a'], generated_from: ['#ffb347','#ff5577'],
454
+ prevents: ['#ff5577','#ff8866'], recommends: ['#3dffa0','#47e5ff'],
455
+ cross_promotes: ['#b47aff','#ffb347'], informs: ['#47e5ff','#5b9cff'],
456
+ engages_with: ['#ffb347','#3dffa0'], co_occurs: ['#5b9cff','#47e5ff']
457
+ };
458
+ const DEFAULT_COLOR = '#47e5ff';
459
+
460
+ const nodeMap = new Map();
461
+ const graphEdges = [];
462
+ for (const e of edges) {
463
+ 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 });
464
+ 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 });
465
+ nodeMap.get(e.s).connections++;
466
+ nodeMap.get(e.t).connections++;
467
+ graphEdges.push({ source: nodeMap.get(e.s), target: nodeMap.get(e.t), type: e.type, weight: e.w });
468
+ }
469
+ const nodes = [...nodeMap.values()];
470
+
471
+ function resize() {
472
+ dpr = window.devicePixelRatio || 1;
473
+ W = container.clientWidth;
474
+ H = 650;
475
+ canvas.width = W * dpr;
476
+ canvas.height = H * dpr;
477
+ canvas.style.width = W + 'px';
478
+ canvas.style.height = H + 'px';
479
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
480
+ }
481
+ resize();
482
+ window.addEventListener('resize', resize);
483
+
484
+ const typeGroups = {};
485
+ for (const n of nodes) {
486
+ if (!typeGroups[n.type]) typeGroups[n.type] = [];
487
+ typeGroups[n.type].push(n);
488
+ }
489
+ const types = Object.keys(typeGroups);
490
+ types.forEach((t, i) => {
491
+ const angle = (i / types.length) * Math.PI * 2;
492
+ const cx = W/2 + Math.cos(angle) * W * 0.2;
493
+ const cy = H/2 + Math.sin(angle) * H * 0.2;
494
+ for (const n of typeGroups[t]) {
495
+ n.x = cx + (Math.random() - 0.5) * W * 0.25;
496
+ n.y = cy + (Math.random() - 0.5) * H * 0.25;
497
+ }
498
+ });
499
+
500
+ const REPULSION = 4000;
501
+ const ATTRACTION = 0.006;
502
+ const DAMPING = 0.88;
503
+ const CENTER_GRAVITY = 0.0015;
504
+ let hovered = null;
505
+ let dragging = null;
506
+ let dragOff = {x:0,y:0};
507
+
508
+ function simulate() {
509
+ for (let i = 0; i < nodes.length; i++) {
510
+ for (let j = i + 1; j < nodes.length; j++) {
511
+ let dx = nodes[i].x - nodes[j].x;
512
+ let dy = nodes[i].y - nodes[j].y;
513
+ let dist = Math.sqrt(dx*dx + dy*dy) || 1;
514
+ let force = REPULSION / (dist * dist);
515
+ let fx = (dx / dist) * force;
516
+ let fy = (dy / dist) * force;
517
+ nodes[i].vx += fx; nodes[i].vy += fy;
518
+ nodes[j].vx -= fx; nodes[j].vy -= fy;
519
+ }
520
+ }
521
+ for (const e of graphEdges) {
522
+ let dx = e.target.x - e.source.x;
523
+ let dy = e.target.y - e.source.y;
524
+ let dist = Math.sqrt(dx*dx + dy*dy) || 1;
525
+ let force = (dist - 120) * ATTRACTION * e.weight;
526
+ let fx = (dx / dist) * force;
527
+ let fy = (dy / dist) * force;
528
+ e.source.vx += fx; e.source.vy += fy;
529
+ e.target.vx -= fx; e.target.vy -= fy;
530
+ }
531
+ for (const n of nodes) {
532
+ n.vx += (W/2 - n.x) * CENTER_GRAVITY;
533
+ n.vy += (H/2 - n.y) * CENTER_GRAVITY;
534
+ }
535
+ for (const n of nodes) {
536
+ if (n === dragging) continue;
537
+ n.vx *= DAMPING; n.vy *= DAMPING;
538
+ n.x += n.vx; n.y += n.vy;
539
+ n.x = Math.max(30, Math.min(W - 30, n.x));
540
+ n.y = Math.max(30, Math.min(H - 30, n.y));
541
+ }
542
+ }
543
+
544
+ function getNodeRadius(n) {
545
+ return Math.min(20, 4 + Math.sqrt(n.connections) * 3.5);
546
+ }
547
+
548
+ function draw() {
549
+ frame++;
550
+ ctx.clearRect(0, 0, W, H);
551
+
552
+ for (const e of graphEdges) {
553
+ const alpha = 0.1 + e.weight * 0.4;
554
+ const colors = EDGE_COLORS[e.type] || ['#5b9cff','#47e5ff'];
555
+ const grad = ctx.createLinearGradient(e.source.x, e.source.y, e.target.x, e.target.y);
556
+ grad.addColorStop(0, colors[0] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
557
+ grad.addColorStop(1, colors[1] + Math.round(Math.min(0.7,alpha)*255).toString(16).padStart(2,'0'));
558
+ ctx.strokeStyle = grad;
559
+ ctx.lineWidth = 0.5 + e.weight * 2.5;
560
+ ctx.beginPath();
561
+ ctx.moveTo(e.source.x, e.source.y);
562
+ ctx.lineTo(e.target.x, e.target.y);
563
+ ctx.stroke();
564
+ }
565
+
566
+ for (const n of nodes) {
567
+ const r = getNodeRadius(n);
568
+ const color = NODE_COLORS[n.type] || DEFAULT_COLOR;
569
+ const isHover = n === hovered || n === dragging;
570
+ const isHub = n.connections >= 5;
571
+
572
+ const glowSize = isHover ? 30 : (isHub ? 15 + Math.sin(frame * 0.03 + n.x) * 5 : 8);
573
+ ctx.shadowColor = color;
574
+ ctx.shadowBlur = glowSize;
575
+
576
+ if (isHub || isHover) {
577
+ const pulseR = r + 3 + (isHub ? Math.sin(frame * 0.04 + n.y) * 2 : 0);
578
+ ctx.strokeStyle = color;
579
+ ctx.globalAlpha = isHover ? 0.6 : 0.25;
580
+ ctx.lineWidth = 1.5;
581
+ ctx.beginPath();
582
+ ctx.arc(n.x, n.y, pulseR, 0, Math.PI * 2);
583
+ ctx.stroke();
584
+ }
585
+
586
+ ctx.globalAlpha = isHover ? 1 : 0.85;
587
+ ctx.fillStyle = color;
588
+ ctx.beginPath();
589
+ ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
590
+ ctx.fill();
591
+
592
+ ctx.globalAlpha = 0.3;
593
+ ctx.fillStyle = '#ffffff';
594
+ ctx.beginPath();
595
+ ctx.arc(n.x - r * 0.25, n.y - r * 0.25, r * 0.4, 0, Math.PI * 2);
596
+ ctx.fill();
597
+
598
+ ctx.globalAlpha = 1;
599
+ ctx.shadowBlur = 0;
600
+
601
+ if (isHover || n.connections >= 4) {
602
+ const label = n.id;
603
+ ctx.fillStyle = '#e8eaf6';
604
+ ctx.font = (isHover ? 'bold 12px' : '10px') + ' Inter, system-ui, sans-serif';
605
+ ctx.textAlign = 'center';
606
+ ctx.globalAlpha = isHover ? 1 : 0.7;
607
+ ctx.fillText(isHover ? n.id : n.type, n.x, n.y - r - 8);
608
+ ctx.globalAlpha = 1;
609
+ }
610
+ }
611
+ simulate();
612
+ requestAnimationFrame(draw);
613
+ }
614
+ draw();
615
+
616
+ const tooltip = document.getElementById('graph-tooltip');
617
+ function getNodeAt(mx, my) {
618
+ for (let i = nodes.length - 1; i >= 0; i--) {
619
+ const n = nodes[i], r = getNodeRadius(n);
620
+ if (Math.hypot(mx - n.x, my - n.y) <= r + 4) return n;
621
+ }
622
+ return null;
623
+ }
624
+ function getPos(e) {
625
+ const rect = canvas.getBoundingClientRect();
626
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
627
+ }
628
+ canvas.addEventListener('mousemove', function(e) {
629
+ const p = getPos(e);
630
+ if (dragging) {
631
+ dragging.x = p.x + dragOff.x;
632
+ dragging.y = p.y + dragOff.y;
633
+ dragging.vx = 0; dragging.vy = 0;
634
+ return;
635
+ }
636
+ const n = getNodeAt(p.x, p.y);
637
+ hovered = n;
638
+ canvas.style.cursor = n ? 'pointer' : 'grab';
639
+ if (n) {
640
+ const conns = graphEdges.filter(e => e.source === n || e.target === n);
641
+ const types = {};
642
+ conns.forEach(c => { types[c.type] = (types[c.type]||0)+1; });
643
+ const typeStr = Object.entries(types).map(([t,c]) => t+': '+c).join(', ');
644
+ tooltip.innerHTML = '<strong>' + n.id + '</strong><br>' + conns.length + ' connections<br><span style="color:var(--text3);font-size:.75rem">' + typeStr + '</span>';
645
+ tooltip.style.display = 'block';
646
+ tooltip.style.left = (p.x + 15) + 'px';
647
+ tooltip.style.top = (p.y - 10) + 'px';
648
+ } else {
649
+ tooltip.style.display = 'none';
650
+ }
651
+ });
652
+ canvas.addEventListener('mousedown', function(e) {
653
+ const p = getPos(e);
654
+ const n = getNodeAt(p.x, p.y);
655
+ if (n) {
656
+ dragging = n;
657
+ dragOff = { x: n.x - p.x, y: n.y - p.y };
658
+ canvas.style.cursor = 'grabbing';
659
+ }
660
+ });
661
+ canvas.addEventListener('mouseup', function() { dragging = null; });
662
+ canvas.addEventListener('mouseleave', function() { dragging = null; hovered = null; tooltip.style.display = 'none'; });
663
+ })();
664
+ </script>
665
+ </body>
666
+ </html>
@@ -0,0 +1,15 @@
1
+ import type { IpcRouter } from '../ipc/router.js';
2
+ interface ApiServerOptions {
3
+ port: number;
4
+ router: IpcRouter;
5
+ apiKey?: string;
6
+ }
7
+ export declare class ApiServer {
8
+ private opts;
9
+ private server;
10
+ private logger;
11
+ constructor(opts: ApiServerOptions);
12
+ start(): void;
13
+ stop(): void;
14
+ }
15
+ export {};