six-degrees 0.4.3 → 0.4.5

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 (279) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +3 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +41 -13
  5. package/.next/standalone/.next/routes-manifest.json +12 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +18 -17
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +18 -17
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -13
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
  38. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/social/route/app-paths-manifest.json +3 -0
  44. package/.next/standalone/.next/server/app/api/social/route/build-manifest.json +12 -0
  45. package/.next/standalone/.next/server/app/api/social/route/server-reference-manifest.json +4 -0
  46. package/.next/standalone/.next/server/app/api/social/route.js +8 -0
  47. package/.next/standalone/.next/server/app/api/social/route.js.map +5 -0
  48. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -0
  49. package/.next/standalone/.next/server/app/api/social/route_client-reference-manifest.js +3 -0
  50. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/import/page.js +1 -1
  56. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/import.html +1 -1
  59. package/.next/standalone/.next/server/app/import.rsc +22 -21
  60. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +22 -21
  61. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  62. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -15
  63. package/.next/standalone/.next/server/app/index.html +1 -1
  64. package/.next/standalone/.next/server/app/index.rsc +20 -19
  65. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -15
  66. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +20 -19
  67. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  68. package/.next/standalone/.next/server/app/launch/page.js +1 -1
  69. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/launch.html +2 -2
  72. package/.next/standalone/.next/server/app/launch.rsc +22 -21
  73. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +22 -21
  74. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  75. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -15
  76. package/.next/standalone/.next/server/app/page.js +1 -1
  77. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/paths/page.js +1 -1
  80. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/app/paths.html +1 -1
  83. package/.next/standalone/.next/server/app/paths.rsc +22 -21
  84. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +22 -21
  85. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  86. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -15
  87. package/.next/standalone/.next/server/app/profile/page.js +1 -1
  88. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  90. package/.next/standalone/.next/server/app/profile.html +1 -1
  91. package/.next/standalone/.next/server/app/profile.rsc +22 -21
  92. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +22 -21
  93. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  94. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -15
  95. package/.next/standalone/.next/server/app/queue/page.js +1 -1
  96. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  98. package/.next/standalone/.next/server/app/queue.html +1 -1
  99. package/.next/standalone/.next/server/app/queue.rsc +22 -21
  100. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +22 -21
  101. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  102. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +16 -15
  103. package/.next/standalone/.next/server/app/scores/page.js +1 -1
  104. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/scores.html +1 -1
  107. package/.next/standalone/.next/server/app/scores.rsc +22 -21
  108. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +22 -21
  109. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
  110. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -15
  111. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  112. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  113. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  114. package/.next/standalone/.next/server/app/settings.html +1 -1
  115. package/.next/standalone/.next/server/app/settings.rsc +22 -21
  116. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +22 -21
  117. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  118. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -15
  119. package/.next/standalone/.next/server/app/setup/page.js +1 -1
  120. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/setup.html +1 -1
  123. package/.next/standalone/.next/server/app/setup.rsc +22 -21
  124. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +22 -21
  125. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  126. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -15
  127. package/.next/standalone/.next/server/app/social/page/app-paths-manifest.json +3 -0
  128. package/.next/standalone/.next/server/app/social/page/build-manifest.json +20 -0
  129. package/.next/standalone/.next/server/app/social/page/next-font-manifest.json +11 -0
  130. package/.next/standalone/.next/server/app/social/page/react-loadable-manifest.json +1 -0
  131. package/.next/standalone/.next/server/app/social/page/server-reference-manifest.json +4 -0
  132. package/.next/standalone/.next/server/app/social/page.js +13 -0
  133. package/.next/standalone/.next/server/app/social/page.js.map +5 -0
  134. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -0
  135. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +3 -0
  136. package/.next/standalone/.next/server/app/social.html +1 -0
  137. package/.next/standalone/.next/server/app/social.meta +26 -0
  138. package/.next/standalone/.next/server/app/social.rsc +26 -0
  139. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +26 -0
  140. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +4 -0
  141. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +30 -0
  142. package/.next/standalone/.next/server/app-paths-manifest.json +3 -1
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17kn1mk._.js +8 -0
  145. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0uag-k5._.js → [root-of-the-server]__1dmc7-t._.js} +1 -1
  146. package/.next/standalone/.next/server/chunks/_1_fynia._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/_1xh07bp._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_social_route_actions_1_3c6qn.js +3 -0
  150. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +1 -1
  151. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16hs8kw._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1feicqq._.js +3 -0
  153. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jeztd-._.js +3 -0
  154. package/.next/standalone/.next/server/chunks/ssr/_005q9n1._.js +3 -0
  155. package/.next/standalone/.next/server/chunks/ssr/_0ezj4e0._.js +3 -0
  156. package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +3 -0
  157. package/.next/standalone/.next/server/chunks/ssr/{_0hw_qnj._.js → _0wxbq78._.js} +1 -1
  158. package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +3 -0
  160. package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +3 -0
  161. package/.next/standalone/.next/server/chunks/ssr/{_1cyxocj._.js → _1ap1tu_._.js} +2 -2
  162. package/.next/standalone/.next/server/chunks/ssr/_1mj739k._.js +3 -0
  163. package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +3 -0
  164. package/.next/standalone/.next/server/chunks/ssr/_217nddo._.js +3 -0
  165. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_social_page_actions_176i1kl.js +3 -0
  166. package/.next/standalone/.next/server/chunks/ssr/app_0i3fw_m._.js +3 -0
  167. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +17 -4
  168. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  169. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +1 -1
  170. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +1 -1
  171. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +3 -0
  172. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
  173. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  174. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  175. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  176. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  177. package/.next/standalone/.next/server/pages/404.html +1 -1
  178. package/.next/standalone/.next/server/pages/500.html +1 -1
  179. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  180. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  181. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  182. package/.next/standalone/.next/static/chunks/04a3zockahetx.js +1 -0
  183. package/.next/standalone/.next/static/chunks/{2wwmljnf7yn_5.js → 0a5iadnmt9u9k.js} +1 -1
  184. package/.next/standalone/.next/static/chunks/0g_p4p7oci18u.js +1 -0
  185. package/.next/standalone/.next/static/chunks/0y1yms5kez2f3.js +1 -0
  186. package/.next/standalone/.next/static/chunks/14m1zu-8warte.js +1 -0
  187. package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +1 -0
  188. package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +1 -0
  189. package/.next/standalone/.next/static/chunks/{1zwjy1mmpkdrg.js → 24m745q_85u34.js} +2 -2
  190. package/.next/standalone/.next/static/chunks/2_zu7rmb8flzl.js +2 -0
  191. package/.next/standalone/.next/static/chunks/38wwtni5e1ec1.js +31 -0
  192. package/.next/standalone/.next/static/chunks/3p_pwu5gx2i9h.js +1 -0
  193. package/.next/{static/chunks/0_3ubivay59bq.js → standalone/.next/static/chunks/3zw8bv7-mc4n6.js} +1 -1
  194. package/.next/standalone/CHANGELOG.md +58 -0
  195. package/.next/standalone/app/api/network/route.js +11 -0
  196. package/.next/standalone/app/api/scraper/route.js +2 -0
  197. package/.next/standalone/app/api/social/route.js +96 -0
  198. package/.next/standalone/app/components/AutoScanButton.js +41 -0
  199. package/.next/standalone/app/components/ChainView.js +7 -0
  200. package/.next/standalone/app/components/FilterPanel.js +6 -0
  201. package/.next/standalone/app/components/ForceGraph.js +257 -39
  202. package/.next/standalone/app/components/GalaxyLab.js +275 -0
  203. package/.next/standalone/app/components/ScanStatusBar.js +22 -8
  204. package/.next/standalone/app/components/SeparationView.js +9 -1
  205. package/.next/standalone/app/components/Sidebar.js +88 -26
  206. package/.next/standalone/app/components/SocialAutoSync.js +34 -0
  207. package/.next/standalone/app/components/settings/NetworkHealthSection.js +85 -0
  208. package/.next/standalone/app/layout.js +3 -0
  209. package/.next/standalone/app/page.js +29 -0
  210. package/.next/standalone/app/scores/page.js +3 -0
  211. package/.next/standalone/app/setup/page.js +1 -1
  212. package/.next/standalone/app/social/page.js +281 -0
  213. package/.next/standalone/lib/experimental-client.js +38 -0
  214. package/.next/standalone/lib/galaxy-export.js +152 -0
  215. package/.next/standalone/lib/galaxy-lab.js +296 -0
  216. package/.next/standalone/lib/insights.js +54 -0
  217. package/.next/standalone/lib/linkedin-export.js +156 -0
  218. package/.next/standalone/lib/network-health.js +42 -0
  219. package/.next/standalone/lib/network.js +1 -0
  220. package/.next/standalone/package-lock.json +2 -2
  221. package/.next/standalone/package.json +1 -1
  222. package/.next/standalone/scripts/scrape.py +118 -2
  223. package/.next/static/chunks/04a3zockahetx.js +1 -0
  224. package/.next/static/chunks/{2wwmljnf7yn_5.js → 0a5iadnmt9u9k.js} +1 -1
  225. package/.next/static/chunks/0g_p4p7oci18u.js +1 -0
  226. package/.next/static/chunks/0y1yms5kez2f3.js +1 -0
  227. package/.next/static/chunks/14m1zu-8warte.js +1 -0
  228. package/.next/static/chunks/1e1n0kkz-psdb.js +1 -0
  229. package/.next/static/chunks/236f0jtp68j_h.js +1 -0
  230. package/.next/static/chunks/{1zwjy1mmpkdrg.js → 24m745q_85u34.js} +2 -2
  231. package/.next/static/chunks/2_zu7rmb8flzl.js +2 -0
  232. package/.next/static/chunks/38wwtni5e1ec1.js +31 -0
  233. package/.next/static/chunks/3p_pwu5gx2i9h.js +1 -0
  234. package/.next/{standalone/.next/static/chunks/0_3ubivay59bq.js → static/chunks/3zw8bv7-mc4n6.js} +1 -1
  235. package/CHANGELOG.md +58 -0
  236. package/lib/experimental-client.js +38 -0
  237. package/lib/galaxy-export.js +152 -0
  238. package/lib/galaxy-lab.js +296 -0
  239. package/lib/insights.js +54 -0
  240. package/lib/linkedin-export.js +156 -0
  241. package/lib/network-health.js +42 -0
  242. package/lib/network.js +1 -0
  243. package/package.json +1 -1
  244. package/scripts/scrape.py +118 -2
  245. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d3vjsq._.js +0 -3
  246. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m4vk0b._.js +0 -3
  247. package/.next/standalone/.next/server/chunks/ssr/_012mop3._.js +0 -3
  248. package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/ssr/_1163x49._.js +0 -3
  250. package/.next/standalone/.next/server/chunks/ssr/_1av11ws._.js +0 -3
  251. package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +0 -3
  252. package/.next/standalone/.next/server/chunks/ssr/_1ig7em7._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +0 -3
  256. package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +0 -1
  257. package/.next/standalone/.next/static/chunks/0f4ugd6v3ph0-.js +0 -1
  258. package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +0 -1
  259. package/.next/standalone/.next/static/chunks/10ogurbcuf3nq.js +0 -1
  260. package/.next/standalone/.next/static/chunks/1489t7jrjojk_.js +0 -1
  261. package/.next/standalone/.next/static/chunks/1zunq4tk-u3ng.js +0 -1
  262. package/.next/standalone/.next/static/chunks/2p5yt1o9tnxjn.js +0 -2
  263. package/.next/standalone/.next/static/chunks/2pyo4j84prgp6.js +0 -18
  264. package/.next/standalone/.next/static/chunks/3kllnkb3hof1s.js +0 -1
  265. package/.next/static/chunks/0cuuo2jwc9h94.js +0 -1
  266. package/.next/static/chunks/0f4ugd6v3ph0-.js +0 -1
  267. package/.next/static/chunks/0l1568qff0efj.js +0 -1
  268. package/.next/static/chunks/10ogurbcuf3nq.js +0 -1
  269. package/.next/static/chunks/1489t7jrjojk_.js +0 -1
  270. package/.next/static/chunks/1zunq4tk-u3ng.js +0 -1
  271. package/.next/static/chunks/2p5yt1o9tnxjn.js +0 -2
  272. package/.next/static/chunks/2pyo4j84prgp6.js +0 -18
  273. package/.next/static/chunks/3kllnkb3hof1s.js +0 -1
  274. /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_buildManifest.js +0 -0
  275. /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_clientMiddlewareManifest.js +0 -0
  276. /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_ssgManifest.js +0 -0
  277. /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_buildManifest.js +0 -0
  278. /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_clientMiddlewareManifest.js +0 -0
  279. /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_ssgManifest.js +0 -0
@@ -1,11 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
4
4
  import * as d3 from 'd3';
5
5
  import { localPhoto } from '../../lib/photos';
6
6
  import { recentre } from '../../lib/galaxy';
7
7
  import { reachIndex, readyByCircle, scanBars } from '../../lib/reach';
8
8
  import { ringSegments, RING } from '../../lib/dot-rings';
9
+ import { LAB_DEFAULTS, labNow, watchLab, effectiveLab, clockNow, watchClock, setClock, stopReplay, bornTimes, reachCounts, colourScheme, findMatches, loadSocial, chapterAt } from '../../lib/galaxy-lab';
10
+ import { registerGalaxy } from '../../lib/galaxy-export';
9
11
 
10
12
  // Connection fields are attacker-reachable: /api/ingest and /api/update-images
11
13
  // accept writes, and a page on any other site can POST to this app on localhost.
@@ -55,6 +57,13 @@ const RING_CSS = `
55
57
  50% { opacity: 1; transform: scale(1.12); }
56
58
  }
57
59
  .galaxy-ring-pulse { animation: galaxy-ring-pulse 1.8s ease-in-out infinite; }
60
+ .lab-focus .gn:not(.lit), .lab-focus .gl:not(.lit) { opacity: 0.06; }
61
+ .lab-focus .gl.lit { stroke-opacity: 0.7; }
62
+ .lab-focus .dot-rings, .lab-focus .catalyst-ring { opacity: 0.15; }
63
+ .lab-find .gn:not(.found), .lab-find .gl:not(.found) { opacity: 0.07; }
64
+ .lab-find .gn.found { stroke: #fff; stroke-width: 2px; }
65
+ @keyframes lab-pop { from { opacity: 0; } }
66
+ @media (prefers-reduced-motion: no-preference) { .lab-pop .gn, .lab-pop .gl { animation: lab-pop 0.5s ease-out; } }
58
67
  @media (prefers-reduced-motion: reduce) {
59
68
  .galaxy-ring-pulse { animation: none; opacity: 0.8; }
60
69
  }`;
@@ -81,6 +90,36 @@ export default function ForceGraph({ connections, onSelect, tierColors, focusNod
81
90
  const viewRef = useRef({ transform: null, size: null });
82
91
  const sizeRef = useRef(null);
83
92
  const [dimensions, setDimensions] = useState(null);
93
+ const stampRef = useRef(null);
94
+
95
+ // The physics lab (lib/galaxy-lab.js): a slider moves the layout in place,
96
+ // and the replay's clock hides whoever wasn't there yet. Neither rebuilds.
97
+ const lab = useSyncExternalStore(watchLab, labNow, () => LAB_DEFAULTS);
98
+ const labRef = useRef(lab);
99
+ useEffect(() => {
100
+ labRef.current = lab;
101
+ sceneRef.current?.setLab(effectiveLab(lab));
102
+ const c = clockNow();
103
+ sceneRef.current?.setFind(c.find, c.fly);
104
+ if (lab.on) loadSocial();
105
+ }, [lab]);
106
+ useEffect(() => watchClock(() => {
107
+ const c = clockNow();
108
+ sceneRef.current?.setTime(c.at);
109
+ sceneRef.current?.setFind(c.find, c.fly);
110
+ }), []);
111
+ useEffect(() => () => stopReplay(), []);
112
+
113
+ // The dots' colours (Colour by, in the lab): tier as always, or degree,
114
+ // company or warmth. A new scheme recolours in place.
115
+ const social = useSyncExternalStore(watchClock, () => clockNow().social, () => undefined);
116
+ const colourBy = effectiveLab(lab).colourBy;
117
+ const scheme = useMemo(() => colourScheme(colourBy, connections, tierColors, social), [colourBy, connections, tierColors, social]);
118
+ const schemeRef = useRef(scheme);
119
+ useEffect(() => {
120
+ schemeRef.current = scheme;
121
+ sceneRef.current?.setColours(scheme);
122
+ }, [scheme]);
84
123
 
85
124
  // The scene is rebuilt from scratch whenever its inputs change, so only real
86
125
  // changes should count. A parent re-rendering hands over a new onSelect every
@@ -139,9 +178,18 @@ export default function ForceGraph({ connections, onSelect, tierColors, focusNod
139
178
  const saved = transform ? recentre(transform, setFor, size) : null;
140
179
 
141
180
  const select = (d) => onSelectRef.current?.(d);
142
- const scene = renderNetworkMode(svg, ringRef.current, size, connections, select, tierColors, focusNodeRef, saved, viewRef, userName, marks);
181
+ const scene = renderNetworkMode(svg, ringRef.current, size, connections, select, tierColors, focusNodeRef, saved, viewRef, userName, marks, effectiveLab(labRef.current), stampRef.current, schemeRef.current);
143
182
  sceneRef.current = scene;
144
183
  scene.select(selectedIdRef.current);
184
+ setClock({ min: scene.range.min, max: scene.range.max, of: scene.range.of });
185
+ scene.setTime(clockNow().at);
186
+ scene.setFind(clockNow().find, clockNow().fly);
187
+ // Save picture and Record replay (lib/galaxy-export.js) draw from this <svg>.
188
+ const unregister = registerGalaxy({
189
+ svg: svgRef.current,
190
+ stamp: () => (stampRef.current?.style.display === 'block' ? stampRef.current.textContent : null),
191
+ legend: () => schemeRef.current.legend,
192
+ });
145
193
 
146
194
  return () => {
147
195
  // Stop the force simulation this render started.
@@ -151,6 +199,7 @@ export default function ForceGraph({ connections, onSelect, tierColors, focusNod
151
199
  // the same nodes, so they fought each other and the graph shook.
152
200
  // Refreshing made it worse because nothing ever stopped the old ones.
153
201
  scene.simulation.stop();
202
+ unregister();
154
203
  // And any glide or fly-to still under way, which would otherwise go on
155
204
  // moving the next scene's view. viewRef already holds where it was going.
156
205
  svg.interrupt();
@@ -174,11 +223,17 @@ export default function ForceGraph({ connections, onSelect, tierColors, focusNod
174
223
  }}>
175
224
  <div className="galaxy-ring-pulse" style={{ boxSizing: 'border-box', border: '2.5px solid', borderRadius: '50%' }} />
176
225
  </div>
177
- <div style={{ position: 'absolute', bottom: 20, left: 20, display: 'flex', gap: 12, fontSize: 11, color: '#888' }}>
178
- {Object.entries(tierColors).map(([tier, color]) => (
179
- <span key={tier} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
226
+ {/* The replay's date, written by the scene as it plays. */}
227
+ <div ref={stampRef} style={{
228
+ position: 'absolute', bottom: 56, left: '50%', transform: 'translateX(-50%)', display: 'none',
229
+ padding: '6px 14px', borderRadius: 16, background: 'rgba(10,15,30,0.8)', border: '1px solid rgba(255,255,255,0.12)',
230
+ color: '#e6edf5', fontSize: 13, fontWeight: 600, pointerEvents: 'none', fontVariantNumeric: 'tabular-nums',
231
+ }} />
232
+ <div style={{ position: 'absolute', bottom: 20, left: 20, right: 20, display: 'flex', flexWrap: 'wrap', gap: '4px 12px', fontSize: 11, color: '#888', pointerEvents: 'none' }}>
233
+ {scheme.legend.map(([label, color]) => (
234
+ <span key={label} style={{ display: 'flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap' }}>
180
235
  <span style={{ width: 8, height: 8, borderRadius: '50%', background: color, display: 'inline-block' }} />
181
- {tier}
236
+ {label}
182
237
  </span>
183
238
  ))}
184
239
  </div>
@@ -216,7 +271,8 @@ function easeRadius(el, r) {
216
271
  // Graph coordinates put you at 0,0, whatever the size of the box; the zoom
217
272
  // transform places that in the box. So a new size only moves the view, and a
218
273
  // rebuild can start from the view as it was.
219
- function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, focusNodeRef, savedTransform, viewRef, userName, marks = new Map()) {
274
+ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, focusNodeRef, savedTransform, viewRef, userName, marks = new Map(), lab = LAB_DEFAULTS, stamp = null, scheme = null) {
275
+ let colourOf = scheme?.of ?? ((d) => tierColors[d.tier] || '#666');
220
276
  const centerNode = {
221
277
  id: CENTER_ID, name: userName || 'You', tier: 'center', degree: 0,
222
278
  power_score: 10, fx: 0, fy: 0,
@@ -285,16 +341,24 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
285
341
  return SLOTS[slot][t < 0 ? 4 : t];
286
342
  };
287
343
 
344
+ // A phone. Judged by the window, as the page judges it, not by the graph's
345
+ // own box: that narrowed whenever a panel opened on a laptop.
346
+ const isMobileGraph = window.innerWidth < 768;
347
+
348
+ // The physics lab's settings (lib/galaxy-lab.js); today's layout when it's
349
+ // off, and always on a phone, whose layout has no physics.
350
+ let L = isMobileGraph ? { ...LAB_DEFAULTS, labels: lab.labels } : lab;
351
+ const reach = reachCounts(nodes, parentOf);
352
+
288
353
  const nodeRadius = (d) => {
289
354
  if (d.id === CENTER_ID) return 18;
355
+ // Sized by reach, a dot grows with everyone who hangs off it: a 2nd-degree
356
+ // person with a full circle behind them next to one with nobody.
357
+ if (L.sizeBy === 'reach') return Math.min(28, 2.5 + 1.6 * Math.sqrt(reach.get(d.id) || 0)) * L.dotSize;
290
358
  const r = Math.max(3, Math.min(12, (d.power_score || 1) * 1.3));
291
- return d.degree > 1 ? Math.max(2.5, r * 0.75) : r; // further out, a little smaller
359
+ return (d.degree > 1 ? Math.max(2.5, r * 0.75) : r) * L.dotSize; // further out, a little smaller
292
360
  };
293
361
 
294
- // A phone. Judged by the window, as the page judges it, not by the graph's
295
- // own box: that narrowed whenever a panel opened on a laptop.
296
- const isMobileGraph = window.innerWidth < 768;
297
-
298
362
  const g = svg.append('g');
299
363
 
300
364
  // Faint S–D guide rings where each tier settles (the radial force below), so
@@ -302,9 +366,10 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
302
366
  // Once the layout settles they move to where each tier's dots actually sit
303
367
  // (the median distance out), which the other forces push past that radius.
304
368
  const guideRings = new Map();
369
+ let guidesG = null;
305
370
  if (!isMobileGraph && layered) {
306
371
  // In bands: a faint ring and a label for each step along the chain instead.
307
- const guides = g.append('g').attr('class', 'degree-guides').style('pointer-events', 'none');
372
+ const guides = guidesG = g.append('g').attr('class', 'degree-guides').style('pointer-events', 'none');
308
373
  const names = ['', '1st', '2nd', '3rd', '4th', '5th', '6th'];
309
374
  bands.slice(0, SLOTS.length).forEach((b, slot) => {
310
375
  const r = SLOTS[slot][2];
@@ -315,7 +380,7 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
315
380
  .text(names[b] || `${b}th`);
316
381
  });
317
382
  } else if (!isMobileGraph) {
318
- const guides = g.append('g').attr('class', 'tier-guides').style('pointer-events', 'none');
383
+ const guides = guidesG = g.append('g').attr('class', 'tier-guides').style('pointer-events', 'none');
319
384
  for (const tier of ['S', 'A', 'B', 'C', 'D']) {
320
385
  const r = tierRadius(tier);
321
386
  const ring = guides.append('circle').attr('r', r).attr('fill', 'none')
@@ -371,7 +436,7 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
371
436
  const r = nodeRadius(ringNode) + 8;
372
437
  pulse.style.width = pulse.style.height = `${2 * r + 2.5}px`;
373
438
  pulse.style.margin = `${-(r + 1.25)}px`;
374
- pulse.style.borderColor = tierColors[ringNode.tier] || '#FFD700';
439
+ pulse.style.borderColor = colourOf(ringNode) || '#FFD700';
375
440
  placeRing();
376
441
  };
377
442
 
@@ -421,12 +486,20 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
421
486
  });
422
487
  }
423
488
 
489
+ // Each force is today's value times its slider in the lab (1 when it's off).
490
+ const linkBase = isMobileGraph ? 0 : layered ? 0.06 : 0.1;
491
+ const radialBase = layered ? 0.45 : 0.3;
492
+ const linkDistance = d => (d.near ? Math.max(40, radiusOf(d.target) - radiusOf(d.source)) : radiusOf(d.target)) * L.distance;
493
+ const charge = d => (d.id === CENTER_ID ? -20 : -1) * L.push;
424
494
  const simulation = d3.forceSimulation(nodes)
425
- .force('link', d3.forceLink(links).id(d => d.id).distance(d => (d.near ? Math.max(40, radiusOf(d.target) - radiusOf(d.source)) : radiusOf(d.target))).strength(isMobileGraph ? 0 : layered ? 0.06 : 0.1))
426
- .force('charge', d3.forceManyBody().strength(isMobileGraph ? 0 : (d => d.id === CENTER_ID ? -300 : -15)))
495
+ .force('link', d3.forceLink(links).id(d => d.id).distance(linkDistance).strength(Math.min(1, linkBase * L.pull)))
496
+ .force('charge', d3.forceManyBody().strength(isMobileGraph ? 0 : charge))
427
497
  .force('center', isMobileGraph ? null : d3.forceCenter(0, 0))
498
+ .force('gravityX', isMobileGraph ? null : d3.forceX(0).strength(0.15 * L.gravity))
499
+ .force('gravityY', isMobileGraph ? null : d3.forceY(0).strength(0.15 * L.gravity))
428
500
  .force('collision', isMobileGraph ? null : d3.forceCollide().radius(d => nodeRadius(d) + 2))
429
- .force('radial', isMobileGraph ? null : d3.forceRadial(d => d.id === CENTER_ID ? 0 : radiusOf(d), 0, 0).strength(layered ? 0.45 : 0.3));
501
+ .force('radial', isMobileGraph ? null : d3.forceRadial(d => d.id === CENTER_ID ? 0 : radiusOf(d), 0, 0).strength(radialBase * L.rings));
502
+ guidesG?.attr('opacity', Math.min(1, L.rings));
430
503
  simulation.on('end.guides', () => {
431
504
  for (const [tier, { ring, label }] of guideRings) {
432
505
  const out = nodes.filter((n) => n.tier === tier && n.id !== CENTER_ID).map((n) => Math.hypot(n.x || 0, n.y || 0)).sort((x, y) => x - y);
@@ -441,8 +514,9 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
441
514
  // the colour is one step away. It used to search every node for every link:
442
515
  // about 1.8 s at 30,000 people.
443
516
  const link = g.append('g').selectAll('line').data(links).join('line')
444
- .attr('stroke', d => tierColors[d.target.tier] || '#333')
445
- .attr('stroke-opacity', 0.15).attr('stroke-width', 0.5);
517
+ .attr('class', 'gl')
518
+ .attr('stroke', d => colourOf(d.target) || '#333')
519
+ .attr('stroke-opacity', 0.15).attr('stroke-width', 0.5 * L.lines);
446
520
 
447
521
  // Catalyst outer glow rings (rendered behind the nodes)
448
522
  // A catalyst is the green outline on their dot (design C, with the scan
@@ -471,10 +545,11 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
471
545
  const dotRings = g.append('g').attr('class', 'dot-rings').style('pointer-events', 'none')
472
546
  .selectAll('g').data(ringed).join('g')
473
547
  .attr('transform', d => `translate(${d.x || 0},${d.y || 0})`);
474
- dotRings.each(function (d) {
548
+ function drawDotRing(d) {
475
549
  const { bars, ready } = marks.get(d.id);
476
550
  const r = nodeRadius(d) + 3.5;
477
551
  const el = d3.select(this);
552
+ el.selectAll('*').remove();
478
553
  if (bars != null) {
479
554
  for (const seg of ringSegments(r)) {
480
555
  el.append('path').attr('d', seg.d).attr('fill', 'none').attr('stroke-linecap', 'round')
@@ -487,11 +562,13 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
487
562
  el.append('text').attr('x', at).attr('y', at + 2.5).attr('text-anchor', 'middle')
488
563
  .attr('font-size', 7).attr('font-weight', 700).attr('fill', '#fff').text(ready > 9 ? '9+' : ready);
489
564
  }
490
- });
565
+ }
566
+ dotRings.each(drawDotRing);
491
567
 
492
568
  const node = g.append('g').selectAll('circle').data(nodes).join('circle')
569
+ .attr('class', 'gn')
493
570
  .attr('r', nodeRadius)
494
- .attr('fill', d => d.id === CENTER_ID ? '#fff' : tierColors[d.tier] || '#666')
571
+ .attr('fill', d => d.id === CENTER_ID ? '#fff' : colourOf(d))
495
572
  .attr('fill-opacity', d => (d.degree >= 3 ? 0.55 : d.degree === 2 ? 0.75 : 1))
496
573
  .attr('stroke', d => {
497
574
  if (d.id === CENTER_ID) return '#FFD700';
@@ -518,8 +595,26 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
518
595
  .style('pointer-events', 'none').style('opacity', 0)
519
596
  .style('z-index', 1001).style('transition', 'opacity 0.15s');
520
597
 
598
+ // Their branch: everyone who hangs off them, all the way down, and the chain
599
+ // back to you. Hovering lights it up in the lab, like Obsidian's graph.
600
+ const kids = new Map();
601
+ for (const [child, parent] of parentOf) {
602
+ if (!kids.has(parent)) kids.set(parent, []);
603
+ kids.get(parent).push(child);
604
+ }
605
+ const lightBranch = (d) => {
606
+ const lit = new Set([CENTER_ID, d.id]);
607
+ const stack = [d.id];
608
+ while (stack.length) for (const k of kids.get(stack.pop()) || []) if (!lit.has(k)) { lit.add(k); stack.push(k); }
609
+ for (let p = parentOf.get(d.id), hops = 0; p != null && hops < 7; p = parentOf.get(p), hops++) lit.add(p);
610
+ node.classed('lit', n => lit.has(n.id));
611
+ link.classed('lit', l => lit.has(l.source.id) && lit.has(l.target.id));
612
+ g.classed('lab-focus', true);
613
+ };
614
+
521
615
  node.on('mouseover', function (event, d) {
522
616
  easeRadius(this, nodeRadius(d) * 1.5);
617
+ if (L.on && L.branch && d.id !== CENTER_ID) lightBranch(d);
523
618
  if (d.profile_image_url && d.id !== CENTER_ID) {
524
619
  const size = Math.max(48, nodeRadius(d) * 5);
525
620
  photoTooltip.style('opacity', 1);
@@ -547,27 +642,41 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
547
642
  }
548
643
  }).on('mouseout', function (event, d) {
549
644
  easeRadius(this, nodeRadius(d));
645
+ g.classed('lab-focus', false);
550
646
  tooltip.style('opacity', 0);
551
647
  photoTooltip.style('opacity', 0);
552
648
  });
553
649
 
554
650
  setupDrag(node, simulation, CENTER_ID);
555
651
 
556
- const labels = g.append('g').selectAll('text')
557
- // Names for you, your S-tier connections and catalysts; further out (the
558
- // Degree filter) only on hover, or hundreds of S-tier names pile up.
559
- .data(nodes.filter(n => n.id === CENTER_ID || (n.degree === 1 && (n.tier === 'S' || n.is_catalyst))))
560
- .join('text')
561
- .text(d => d.is_catalyst && d.tier !== 'S' ? d.name + ' ⚡' : d.name)
562
- .attr('font-size', d => d.id === CENTER_ID ? 14 : 10)
563
- .attr('font-weight', d => d.id === CENTER_ID ? 700 : 500)
564
- .attr('fill', d => {
565
- if (d.id === CENTER_ID) return '#fff';
566
- if (d.is_catalyst) return '#00ff88';
567
- return tierColors[d.tier];
568
- })
569
- .attr('text-anchor', 'middle').attr('dy', d => nodeRadius(d) + 14)
570
- .style('pointer-events', 'none');
652
+ // Names for you, your S-tier connections and catalysts; further out (the
653
+ // Degree filter) only on hover, or hundreds of S-tier names pile up. The lab
654
+ // can show all your connections' names, or none.
655
+ // Filter → Names turns them all off; whoever Find matches (40 or fewer) is
656
+ // named either way.
657
+ let findHit = null;
658
+ const labelled = (n) => {
659
+ if (findHit && findHit.size <= 40 && findHit.has(n.id)) return true;
660
+ if (!L.labels) return false;
661
+ return n.id === CENTER_ID || (n.degree === 1 && (L.names === 'all' || n.tier === 'S' || n.is_catalyst));
662
+ };
663
+ const labelsG = g.append('g');
664
+ let labels = labelsG.selectAll('text');
665
+ const drawLabels = () => {
666
+ labels = labelsG.selectAll('text').data(nodes.filter(labelled), d => d.id).join('text')
667
+ .text(d => d.is_catalyst && d.tier !== 'S' ? d.name + ' ⚡' : d.name)
668
+ .attr('font-size', d => d.id === CENTER_ID ? 14 : 10)
669
+ .attr('font-weight', d => d.id === CENTER_ID ? 700 : 500)
670
+ .attr('fill', d => {
671
+ if (d.id === CENTER_ID) return '#fff';
672
+ if (d.is_catalyst) return '#00ff88';
673
+ return colourOf(d);
674
+ })
675
+ .attr('text-anchor', 'middle').attr('dy', d => nodeRadius(d) + 14)
676
+ .attr('x', d => d.x || 0).attr('y', d => d.y || 0)
677
+ .style('pointer-events', 'none');
678
+ };
679
+ drawLabels();
571
680
 
572
681
  const catalystRings = g.selectAll('.catalyst-ring');
573
682
 
@@ -580,7 +689,116 @@ function renderNetworkMode(svg, ring, box, connections, onSelect, tierColors, fo
580
689
  placeRing();
581
690
  });
582
691
 
583
- return { simulation, resize, select };
692
+ // A slider moved: change the running layout in place and let it settle again.
693
+ const setLab = (next) => {
694
+ if (isMobileGraph) {
695
+ if (next.labels !== L.labels) { L = { ...L, labels: next.labels }; drawLabels(); showBorn(); }
696
+ return;
697
+ }
698
+ const prev = L;
699
+ L = next;
700
+ const sized = prev.dotSize !== L.dotSize || prev.sizeBy !== L.sizeBy;
701
+ if (sized) {
702
+ node.attr('r', nodeRadius);
703
+ catalystRings.attr('r', d => nodeRadius(d) + 6);
704
+ dotRings.each(drawDotRing);
705
+ simulation.force('collision').radius(d => nodeRadius(d) + 2);
706
+ if (ringNode) select(ringNode.id);
707
+ }
708
+ if (prev.lines !== L.lines) link.attr('stroke-width', 0.5 * L.lines);
709
+ if (sized || prev.names !== L.names || prev.labels !== L.labels) { drawLabels(); showBorn(); }
710
+ if (!L.on || !L.branch) g.classed('lab-focus', false);
711
+ g.classed('lab-pop', L.on && nodes.length < 5000);
712
+ const forces = ['gravity', 'rings', 'push', 'pull', 'distance'].some(k => prev[k] !== L[k]);
713
+ if (!forces && !sized) return;
714
+ simulation.force('link').distance(linkDistance).strength(Math.min(1, linkBase * L.pull));
715
+ simulation.force('charge').strength(charge);
716
+ simulation.force('gravityX').strength(0.15 * L.gravity);
717
+ simulation.force('gravityY').strength(0.15 * L.gravity);
718
+ simulation.force('radial').strength(radialBase * L.rings);
719
+ guidesG?.attr('opacity', Math.min(1, L.rings));
720
+ simulation.alpha(Math.max(simulation.alpha(), 0.5)).restart();
721
+ };
722
+ g.classed('lab-pop', L.on && nodes.length < 5000);
723
+
724
+ // The replay: whoever wasn't there yet at the clock's time is hidden. Only the
725
+ // dots that change are touched, so a frame costs little at 30,000 people.
726
+ const born = bornTimes(nodes, parentOf);
727
+ const bornById = new Map(nodes.map((n, i) => [n.id, born[i]]));
728
+ const nodeEls = node.nodes();
729
+ const linkEls = link.nodes(); // links[i] is connections[i], which is nodes[i + 1]
730
+ const extraEls = new Map();
731
+ dotRings.each(function (d) { extraEls.set(d.id, [this]); });
732
+ catalystRings.each(function (d) { extraEls.set(d.id, [...(extraEls.get(d.id) || []), this]); });
733
+ const shown = new Uint8Array(nodes.length).fill(1);
734
+ const d1 = nodes.filter(n => n.degree === 1);
735
+ const dated = d1.map(n => bornById.get(n.id)).filter(Number.isFinite);
736
+ const range = { min: dated.length ? dated.reduce((m, t) => Math.min(m, t)) : null, max: dated.length ? dated.reduce((m, t) => Math.max(m, t)) : null, of: d1.length };
737
+ let clockAt = null;
738
+ const showBorn = () => labels.style('display', d => (clockAt == null || bornById.get(d.id) <= clockAt ? null : 'none'));
739
+ const setTime = (at) => {
740
+ if (!L.on) at = null;
741
+ clockAt = at;
742
+ let n1 = 0;
743
+ for (let i = 1; i < nodes.length; i++) {
744
+ const vis = at == null || born[i] <= at ? 1 : 0;
745
+ if (vis && nodes[i].degree === 1) n1++;
746
+ if (vis === shown[i]) continue;
747
+ shown[i] = vis;
748
+ const display = vis ? '' : 'none';
749
+ nodeEls[i].style.display = display;
750
+ if (linkEls[i - 1]) linkEls[i - 1].style.display = display;
751
+ for (const el of extraEls.get(nodes[i].id) || []) el.style.display = display;
752
+ }
753
+ showBorn();
754
+ if (!stamp) return;
755
+ stamp.style.display = at == null ? 'none' : 'block';
756
+ if (at != null) {
757
+ const when = new Date(at).toLocaleDateString(undefined, { month: 'short', year: 'numeric' });
758
+ const job = chapterAt(clockNow().social, at);
759
+ stamp.textContent = `${when} · ${n1.toLocaleString()} of ${range.of.toLocaleString()} connections${job ? ` · at ${job.company}` : ''}`;
760
+ }
761
+ };
762
+
763
+ // A new colour scheme (Colour by): recolour the dots, lines and names in place.
764
+ const setColours = (next) => {
765
+ colourOf = next.of;
766
+ node.attr('fill', d => d.id === CENTER_ID ? '#fff' : colourOf(d));
767
+ link.attr('stroke', d => colourOf(d.target) || '#333');
768
+ drawLabels();
769
+ showBorn();
770
+ if (ringNode) select(ringNode.id);
771
+ };
772
+
773
+ // Find: whoever matches stays lit and the rest dim; each Enter (a new `fly`)
774
+ // flies to the best-scored match and opens their card.
775
+ let lastFind = '';
776
+ let lastFly = clockNow().fly;
777
+ const setFind = (query, fly) => {
778
+ const q = L.on ? String(query || '') : '';
779
+ if (q !== lastFind) {
780
+ lastFind = q;
781
+ findHit = findMatches(nodes.filter(n => n.id !== CENTER_ID), q);
782
+ node.classed('found', n => !!findHit?.has(n.id));
783
+ link.classed('found', l => !!findHit?.has(l.target.id));
784
+ g.classed('lab-find', !!findHit);
785
+ drawLabels();
786
+ showBorn();
787
+ setClock({ found: findHit ? findHit.size : 0 });
788
+ }
789
+ if (fly !== lastFly) {
790
+ lastFly = fly;
791
+ const best = findHit && nodes.filter(n => findHit.has(n.id)).sort((a, b) => (b.power_score || 0) - (a.power_score || 0))[0];
792
+ if (best) {
793
+ const k = 2.5;
794
+ moveView(d3.zoomIdentity.translate(box.width / 2 - best.x * k, box.height / 2 - best.y * k).scale(k), 750);
795
+ select(best.id);
796
+ onSelect(best);
797
+ }
798
+ }
799
+ };
800
+
801
+ return { simulation, resize, select, setLab, setTime, setColours, setFind, range };
584
802
  }
585
803
 
586
804
  function setupDrag(node, simulation, fixedId) {