@sleep2agi/agent-network-dashboard 0.4.6-preview.19 → 0.4.6-preview.191

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 (394) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +41 -41
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +6 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +14 -14
  27. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/servers/route.js +7 -0
  37. package/.next/server/app/api/hub/servers/route.js.map +5 -0
  38. package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +14 -14
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +2 -2
  49. package/.next/server/app/login.rsc +14 -14
  50. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +14 -14
  59. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/messages.html +2 -2
  67. package/.next/server/app/messages.rsc +14 -14
  68. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  74. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/node.html +2 -2
  76. package/.next/server/app/node.rsc +14 -14
  77. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  83. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/nodes.html +2 -2
  85. package/.next/server/app/nodes.rsc +14 -14
  86. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  92. package/.next/server/app/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/server-logs.html +2 -2
  95. package/.next/server/app/server-logs.rsc +14 -14
  96. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  97. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  99. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/networks.html +2 -2
  104. package/.next/server/app/settings/networks.rsc +14 -14
  105. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  106. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  112. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  114. package/.next/server/app/settings/tokens.html +2 -2
  115. package/.next/server/app/settings/tokens.rsc +14 -14
  116. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  117. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  119. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  122. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/settings.html +2 -2
  124. package/.next/server/app/settings.rsc +14 -14
  125. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  131. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  132. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/tasks.html +2 -2
  134. package/.next/server/app/tasks.rsc +14 -14
  135. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  136. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  137. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  138. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  140. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  141. package/.next/server/app-paths-manifest.json +1 -0
  142. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
  143. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
  144. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
  145. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
  146. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  147. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  148. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  149. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  150. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  151. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  160. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  174. package/.next/server/middleware-build-manifest.js +3 -3
  175. package/.next/server/pages/404.html +2 -2
  176. package/.next/server/pages/500.html +1 -1
  177. package/.next/server/server-reference-manifest.js +1 -1
  178. package/.next/server/server-reference-manifest.json +1 -1
  179. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  180. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  181. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  182. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  183. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  184. package/.next/static/chunks/07r1dbd70cp.g.js +1 -0
  185. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  186. package/.next/static/chunks/0agj1oq3ux9.a.css +2 -0
  187. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  188. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  189. package/.next/static/chunks/0h3od5x-eekc_.js +1 -0
  190. package/.next/static/chunks/0k_ko8c_wcaa2.js +4 -0
  191. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  192. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  193. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  194. package/.next/trace +2 -2
  195. package/.next/trace-build +1 -1
  196. package/.next/types/routes.d.ts +2 -1
  197. package/.next/types/validator.ts +9 -0
  198. package/app/api/hub/servers/route.ts +39 -0
  199. package/app/components/ChatPopover.tsx +38 -2
  200. package/app/components/CommandPalette.tsx +107 -0
  201. package/app/components/HelpOverlay.tsx +126 -0
  202. package/app/components/ServersDrawer.tsx +64 -27
  203. package/app/components/TopoGraph.tsx +3781 -272
  204. package/app/components/utils.ts +8 -7
  205. package/app/globals.css +92 -5
  206. package/app/lib/time.ts +36 -0
  207. package/package.json +1 -1
  208. package/scripts/help-overlay-topo-test.mjs +44 -0
  209. package/scripts/servers-drawer-live-test.mjs +96 -0
  210. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  211. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  212. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  213. package/scripts/topo-active-links-chip-test.mjs +73 -0
  214. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  215. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  216. package/scripts/topo-arrival-ping-test.mjs +87 -0
  217. package/scripts/topo-arrow-tier-test.mjs +61 -0
  218. package/scripts/topo-auto-fit-test.mjs +73 -0
  219. package/scripts/topo-canvas-aria-test.mjs +108 -0
  220. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  221. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  222. package/scripts/topo-chip-focus-test.mjs +95 -0
  223. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  224. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  225. package/scripts/topo-click-ripple-test.mjs +66 -0
  226. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  227. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  228. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  229. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  230. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  231. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  232. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  233. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  234. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  235. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  236. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  237. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  238. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  239. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  240. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  241. package/scripts/topo-edge-fadein-test.mjs +106 -0
  242. package/scripts/topo-edge-focus-test.mjs +113 -0
  243. package/scripts/topo-edge-hover-test.mjs +93 -0
  244. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  245. package/scripts/topo-edge-pill-test.mjs +105 -0
  246. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  247. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  248. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  249. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  250. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  251. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  252. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  253. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  254. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  255. package/scripts/topo-fit-key-test.mjs +106 -0
  256. package/scripts/topo-freshness-chip-test.mjs +74 -0
  257. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  258. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  259. package/scripts/topo-group-count-test.mjs +68 -0
  260. package/scripts/topo-group-hover-test.mjs +82 -0
  261. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  262. package/scripts/topo-group-label-hover-test.mjs +119 -0
  263. package/scripts/topo-group-label-tint-test.mjs +99 -0
  264. package/scripts/topo-group-label-title-test.mjs +83 -0
  265. package/scripts/topo-group-pin-test.mjs +119 -0
  266. package/scripts/topo-group-status-pips-test.mjs +103 -0
  267. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  268. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  269. package/scripts/topo-groupbox-live-test.mjs +100 -0
  270. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  271. package/scripts/topo-groupbox-test.mjs +4 -2
  272. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  273. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  274. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  275. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  276. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  277. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  278. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  279. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  280. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  281. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  282. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  283. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  284. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  285. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  286. package/scripts/topo-hub-a11y-test.mjs +112 -0
  287. package/scripts/topo-hub-busyness-test.mjs +100 -0
  288. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  289. package/scripts/topo-hub-fadein-test.mjs +88 -0
  290. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  291. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  292. package/scripts/topo-hub-title-test.mjs +93 -0
  293. package/scripts/topo-hub-working-count-test.mjs +99 -0
  294. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  295. package/scripts/topo-label-lift-test.mjs +119 -0
  296. package/scripts/topo-label-scale-test.mjs +78 -0
  297. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  298. package/scripts/topo-layout-key-test.mjs +91 -0
  299. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  300. package/scripts/topo-layout-toggle-test.mjs +131 -0
  301. package/scripts/topo-legend-count-test.mjs +88 -0
  302. package/scripts/topo-legend-hover-test.mjs +113 -0
  303. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  304. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  305. package/scripts/topo-legend-pin-test.mjs +128 -0
  306. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  307. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  308. package/scripts/topo-legend-row-title-test.mjs +102 -0
  309. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  310. package/scripts/topo-minimap-test.mjs +68 -0
  311. package/scripts/topo-mobile-chips-test.mjs +103 -0
  312. package/scripts/topo-node-a11y-test.mjs +107 -0
  313. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  314. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  315. package/scripts/topo-node-title-peers-test.mjs +107 -0
  316. package/scripts/topo-node-title-test.mjs +101 -0
  317. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  318. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  319. package/scripts/topo-offline-dim-test.mjs +70 -0
  320. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  321. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  322. package/scripts/topo-overlap-test.mjs +32 -5
  323. package/scripts/topo-pan-cursor-test.mjs +62 -0
  324. package/scripts/topo-panel-elevation-test.mjs +76 -0
  325. package/scripts/topo-panel-fadein-test.mjs +102 -0
  326. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  327. package/scripts/topo-particle-stagger-test.mjs +82 -0
  328. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  329. package/scripts/topo-pin-intersection-test.mjs +121 -0
  330. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  331. package/scripts/topo-pin-persistence-test.mjs +105 -0
  332. package/scripts/topo-popover-meta-test.mjs +78 -0
  333. package/scripts/topo-pressure-bar-test.mjs +69 -0
  334. package/scripts/topo-pressure-hover-test.mjs +123 -0
  335. package/scripts/topo-pressure-pin-test.mjs +119 -0
  336. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  337. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  338. package/scripts/topo-pulse-rate-test.mjs +71 -0
  339. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  340. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  341. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  342. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  343. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  344. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  345. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  346. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  347. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  348. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  349. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  350. package/scripts/topo-recent-row-title-test.mjs +112 -0
  351. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  352. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  353. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  354. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  355. package/scripts/topo-reduced-motion-test.mjs +77 -0
  356. package/scripts/topo-reset-spin-test.mjs +120 -0
  357. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  358. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  359. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  360. package/scripts/topo-svg-focus-test.mjs +112 -0
  361. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  362. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  363. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  364. package/scripts/topo-tier-ring-test.mjs +69 -0
  365. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  366. package/scripts/topo-tier-stagger-test.mjs +89 -0
  367. package/scripts/topo-tz-ghost-test.mjs +69 -0
  368. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  369. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  370. package/scripts/topo-vendor-pill-test.mjs +125 -0
  371. package/scripts/topo-vendor-pin-test.mjs +125 -0
  372. package/scripts/topo-view-smooth-test.mjs +131 -0
  373. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  374. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  375. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  376. package/scripts/topo-zoom-keys-test.mjs +89 -0
  377. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  378. package/.next/static/chunks/01~mq1zyvml0g.js +0 -1
  379. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  380. package/.next/static/chunks/03wwb3f68plm9.js +0 -1
  381. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  382. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  383. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  384. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  385. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  386. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  387. package/.next/static/chunks/0q6-hmi4kx~4b.js +0 -1
  388. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  389. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  390. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  391. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  392. /package/.next/static/{ccwEK-yN0YiFK6FI2KHGf → 73xpyKgxUshGvnn998bi4}/_buildManifest.js +0 -0
  393. /package/.next/static/{ccwEK-yN0YiFK6FI2KHGf → 73xpyKgxUshGvnn998bi4}/_clientMiddlewareManifest.js +0 -0
  394. /package/.next/static/{ccwEK-yN0YiFK6FI2KHGf → 73xpyKgxUshGvnn998bi4}/_ssgManifest.js +0 -0
@@ -0,0 +1,150 @@
1
+ /* Round 164 verification: edge midpoint count badge gains
2
+ * hover-lift (6th surface in the hover-elevation idiom family).
3
+ *
4
+ * Pre-R164 the badge had R122 hover→edge-brighten propagation
5
+ * but the badge itself stayed static (r=9, strokeWidth=1 or 2).
6
+ * The hover-elevation family covered nodes/panels/group boxes/
7
+ * recent rows/legend rows but not the canvas edge badges.
8
+ *
9
+ * R164:
10
+ * r = (isHoveredEdge || isPinned) ? 10.5 : 9
11
+ * transition r 180ms ease-out
12
+ * data-edge-badge-lifted = 'true' | 'false'
13
+ *
14
+ * Pin still keeps its R121 stroke change (legendHeadline +
15
+ * width 2). Hover and pin share the radius lift.
16
+ *
17
+ * Test:
18
+ * 1. Mock 4 msgs on alpha→beta (R100 threshold = count >= 3)
19
+ * 2. Probe idle radius = 9
20
+ * 3. Hover the edge line → badge r = 10.5
21
+ * 4. Mouseleave → r = 9 again
22
+ * 5. Click badge → pin, r = 10.5 + stroke change
23
+ */
24
+ import { chromium } from 'playwright';
25
+ import { readFileSync } from 'node:fs';
26
+
27
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
28
+ const browser = await chromium.launch({ headless: true });
29
+
30
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
34
+ });
35
+
36
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
37
+ await ctx.route('**/api/hub/status*', async (route) => {
38
+ const r = await route.fetch();
39
+ const b = await r.json();
40
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
41
+ const mk = (alias) => ({
42
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
43
+ network_id: nid, project_dir: null,
44
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
45
+ });
46
+ // 4 sessions so alpha and beta sit 90° apart on the ring
47
+ // (not 180° apart — which would put alpha→beta midpoint
48
+ // exactly on the hub at 500,330 and let the hub's r=18
49
+ // halo intercept the badge hover). gamma + delta are
50
+ // filler with no flows; the only edge badge that renders
51
+ // is alpha↔beta.
52
+ await route.fulfill({ response: r, json: { ...b, sessions: [
53
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
54
+ ] } });
55
+ });
56
+
57
+ const now = Date.now();
58
+ const msgs = [];
59
+ for (let i = 0; i < 4; i++) {
60
+ msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
61
+ network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
62
+ }
63
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
64
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
65
+
66
+ const page = await ctx.newPage();
67
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
68
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
69
+ await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
70
+ await page.waitForTimeout(400);
71
+
72
+ // Probe idle state
73
+ const idle = await page.evaluate(() => {
74
+ const g = document.querySelector('[data-edge-count-badge]');
75
+ if (!g) return null;
76
+ const c = g.querySelector('circle');
77
+ return {
78
+ r: parseFloat(c.getAttribute('r')),
79
+ lifted: c.getAttribute('data-edge-badge-lifted'),
80
+ stroke: c.getAttribute('stroke'),
81
+ strokeWidth: parseFloat(c.getAttribute('stroke-width')),
82
+ };
83
+ });
84
+
85
+ // Hover the badge (it also propagates to hoveredEdgeKey)
86
+ await page.locator('[data-edge-count-badge]').hover();
87
+ await page.waitForTimeout(300);
88
+ const hovered = await page.evaluate(() => {
89
+ const g = document.querySelector('[data-edge-count-badge]');
90
+ if (!g) return null;
91
+ const c = g.querySelector('circle');
92
+ return {
93
+ r: parseFloat(c.getAttribute('r')),
94
+ lifted: c.getAttribute('data-edge-badge-lifted'),
95
+ };
96
+ });
97
+
98
+ // Move cursor away to a known empty area
99
+ await page.mouse.move(10, 10);
100
+ await page.waitForTimeout(300);
101
+ const idleAgain = await page.evaluate(() => {
102
+ const g = document.querySelector('[data-edge-count-badge]');
103
+ if (!g) return null;
104
+ const c = g.querySelector('circle');
105
+ return {
106
+ r: parseFloat(c.getAttribute('r')),
107
+ lifted: c.getAttribute('data-edge-badge-lifted'),
108
+ };
109
+ });
110
+
111
+ // Click badge → pin
112
+ await page.locator('[data-edge-count-badge]').click({ force: true });
113
+ await page.waitForTimeout(300);
114
+ // Move away to clear hover, then probe pinned state.
115
+ await page.mouse.move(10, 10);
116
+ await page.waitForTimeout(300);
117
+ const pinned = await page.evaluate(() => {
118
+ const g = document.querySelector('[data-edge-count-badge]');
119
+ if (!g) return null;
120
+ const c = g.querySelector('circle');
121
+ return {
122
+ r: parseFloat(c.getAttribute('r')),
123
+ lifted: c.getAttribute('data-edge-badge-lifted'),
124
+ strokeWidth: parseFloat(c.getAttribute('stroke-width')),
125
+ pinnedAttr: g.getAttribute('data-edge-count-badge-pinned'),
126
+ };
127
+ });
128
+
129
+ await browser.close();
130
+
131
+ const results = {
132
+ idle_r_9: idle && Math.abs(idle.r - 9) < 0.01,
133
+ idle_lifted_false: idle && idle.lifted === 'false',
134
+ idle_strokeWidth_1: idle && idle.strokeWidth === 1,
135
+ hover_r_10p5: hovered && Math.abs(hovered.r - 10.5) < 0.01,
136
+ hover_lifted_true: hovered && hovered.lifted === 'true',
137
+ idle_again_r_9: idleAgain && Math.abs(idleAgain.r - 9) < 0.01,
138
+ idle_again_lifted_false: idleAgain && idleAgain.lifted === 'false',
139
+ pinned_r_10p5: pinned && Math.abs(pinned.r - 10.5) < 0.01,
140
+ pinned_lifted_true: pinned && pinned.lifted === 'true',
141
+ pinned_strokeWidth_2: pinned && pinned.strokeWidth === 2,
142
+ pinned_attr_true: pinned && pinned.pinnedAttr === 'true',
143
+ };
144
+ const ok = Object.values(results).every(Boolean);
145
+ console.log(`${ok ? '✅' : '❌'} edge badge lift:`, JSON.stringify(results),
146
+ `\n idle =`, idle,
147
+ `\n hovered =`, hovered,
148
+ `\n idleAgain =`, idleAgain,
149
+ `\n pinned =`, pinned);
150
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,86 @@
1
+ /* Round 100 verification: edges with count >= 3 render a midpoint
2
+ * count badge. The edge stroke width gauges intensity but at high
3
+ * volume "5 vs 12" is hard to read off the line. The badge spells
4
+ * out the integer at the bezier midpoint.
5
+ *
6
+ * Fleet: alpha → beta with 5 messages, gamma → delta with 1, all
7
+ * fresh. Expect one badge for the 5-msg flow, none for the 1-msg
8
+ * flow (count below threshold). Verify badge text matches the
9
+ * link.count and pointer-events:none so the existing R48 edge
10
+ * hitbox stays alive.
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+
18
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
22
+ });
23
+
24
+ const fresh = new Date(Date.now() - 30 * 1000).toISOString();
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ const r = await route.fetch();
27
+ const b = await r.json();
28
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
29
+ const mk = (alias) => ({
30
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
31
+ network_id: nid, project_dir: null,
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
33
+ });
34
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
35
+ });
36
+
37
+ const now = Date.now();
38
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
39
+ id, from_alias, to_alias, content: 'ping', network_id: 'default',
40
+ created_at: new Date(now - ageMs).toISOString(),
41
+ });
42
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
43
+ // 5 alpha → beta (count=5, badge expected)
44
+ mkMsg('m1', 'alpha', 'beta', 30 * 1000),
45
+ mkMsg('m2', 'alpha', 'beta', 40 * 1000),
46
+ mkMsg('m3', 'alpha', 'beta', 50 * 1000),
47
+ mkMsg('m4', 'alpha', 'beta', 60 * 1000),
48
+ mkMsg('m5', 'alpha', 'beta', 70 * 1000),
49
+ // 1 gamma → delta (count=1, NO badge)
50
+ mkMsg('m6', 'gamma', 'delta', 80 * 1000),
51
+ // 3 beta → alpha (count=3 — threshold, badge expected)
52
+ mkMsg('m7', 'beta', 'alpha', 35 * 1000),
53
+ mkMsg('m8', 'beta', 'alpha', 45 * 1000),
54
+ mkMsg('m9', 'beta', 'alpha', 55 * 1000),
55
+ ] } }));
56
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
57
+ const page = await ctx.newPage();
58
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
59
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
60
+ await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
61
+ await page.waitForTimeout(400);
62
+
63
+ const badges = await page.evaluate(() => {
64
+ return [...document.querySelectorAll('[data-edge-count-badge]')].map(g => ({
65
+ key: g.getAttribute('data-edge-count-badge'),
66
+ count: g.querySelector('text')?.textContent || '',
67
+ pe: getComputedStyle(g).pointerEvents,
68
+ }));
69
+ });
70
+
71
+ await browser.close();
72
+
73
+ const byKey = Object.fromEntries(badges.map(b => [b.key, b]));
74
+ const results = {
75
+ twoBadges: badges.length === 2,
76
+ alphaBeta_badge: byKey['alpha->beta']?.count === '5',
77
+ betaAlpha_badge: byKey['beta->alpha']?.count === '3',
78
+ gammaDelta_noBadge: !byKey['gamma->delta'],
79
+ // R121 promoted the badge from passive display to a click-to-pin
80
+ // affordance — assertion flipped from non-interactive to interactive.
81
+ badges_interactive: badges.every(b => b.pe === 'all'),
82
+ };
83
+ const ok = Object.values(results).every(Boolean);
84
+ console.log(`${ok ? '✅' : '❌'} edge-count badge:`, JSON.stringify(results),
85
+ `\n badges=`, badges);
86
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,111 @@
1
+ /* Round 49 verification: hovering a flow edge highlights its two endpoint
2
+ * nodes and dims the rest — the reverse direction of R40 (node→edges) so
3
+ * "who is this edge between" reads at a glance without the tooltip.
4
+ *
5
+ * Sessions: alpha, beta, gamma, delta. Messages: alpha→beta + gamma→delta.
6
+ * - Before hover: all 4 nodes at base opacity (~1.0).
7
+ * - Hover alpha→beta hitbox: alpha + beta opacity ≥ 0.6, gamma + delta
8
+ * drop below 0.4 (composed via `hoveredEdgeEndpoints`).
9
+ * - Move off the edge: opacities return to base.
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const browser = await chromium.launch({ headless: true });
16
+
17
+ // The dashboard chrome (sidebar + header + stats row) pushes the topology
18
+ // SVG well below the fold at width 1280. Height 1500 fits the whole canvas
19
+ // so SVG-local coordinates map to in-viewport screen coordinates.
20
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(() => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ // Ring layout places nodes around the SVG center and edges curve through
26
+ // it — well inside the 1280x900 viewport. Grid layout pushes the lower-
27
+ // row edges below the fold and breaks locator.hover positioning.
28
+ localStorage.setItem('anet-topo-layout', 'ring');
29
+ sessionStorage.setItem('anet_v3_auth', '1');
30
+ } catch {}
31
+ });
32
+
33
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
34
+ await ctx.route('**/api/hub/status*', async (route) => {
35
+ const r = await route.fetch();
36
+ const b = await r.json();
37
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
38
+ const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
39
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
40
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
41
+ }));
42
+ await route.fulfill({ response: r, json: { ...b, sessions } });
43
+ });
44
+ const now = new Date().toISOString();
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
46
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
47
+ { from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
48
+ ] } }));
49
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
52
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
53
+ await page.waitForTimeout(600);
54
+
55
+ const opacities = () => page.evaluate(() => {
56
+ const out = {};
57
+ for (const a of ['alpha', 'beta', 'gamma', 'delta']) {
58
+ const g = document.querySelector(`g[data-node="${a}"]`);
59
+ out[a] = g ? +(g.style.opacity || '1') : null;
60
+ }
61
+ return out;
62
+ });
63
+
64
+ const before = await opacities();
65
+
66
+ // Hover the alpha→beta edge hitbox. The hitbox is a curved <path> with
67
+ // `pointer-events: stroke`, so the cursor only triggers events when it
68
+ // sits on the stroke itself — the bbox centroid is inside an empty region.
69
+ // Sample a real point at half the path's length via getPointAtLength and
70
+ // convert SVG-local coordinates to screen via getScreenCTM, then move the
71
+ // physical mouse there.
72
+ const target = await page.evaluate(() => {
73
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
74
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
75
+ const t = g.querySelector('path[data-edge-hitbox] title');
76
+ if (t && /^alpha → beta/.test(t.textContent || '')) {
77
+ const hb = g.querySelector('path[data-edge-hitbox]');
78
+ const pt = hb.getPointAtLength(hb.getTotalLength() / 2);
79
+ const ctm = hb.getScreenCTM();
80
+ return { x: pt.x * ctm.a + pt.y * ctm.c + ctm.e, y: pt.x * ctm.b + pt.y * ctm.d + ctm.f };
81
+ }
82
+ }
83
+ return null;
84
+ });
85
+ if (!target) { console.log('❌ no alpha→beta hitbox found'); process.exit(1); }
86
+ console.log(' hover target:', target);
87
+ // Move twice — first to an empty spot, then to the curve point — so the
88
+ // browser registers a pointermove from "elsewhere" to the hitbox stroke.
89
+ await page.mouse.move(10, 10);
90
+ await page.mouse.move(target.x, target.y);
91
+ await page.waitForTimeout(350);
92
+ const during = await opacities();
93
+
94
+ // Move far away — over an empty corner — so no node/edge is hovered.
95
+ await page.mouse.move(20, 20);
96
+ await page.waitForTimeout(250);
97
+ const after = await opacities();
98
+
99
+ await browser.close();
100
+ const baseOk = (o) => o >= 0.55; // online nodes at base ~1.0; allow 0.6 floor
101
+ const dimOk = (o) => o !== null && o < 0.4;
102
+ const results = {
103
+ beforeBaseAll: baseOk(before.alpha) && baseOk(before.beta) && baseOk(before.gamma) && baseOk(before.delta),
104
+ hoverKeepsEndpoints: baseOk(during.alpha) && baseOk(during.beta),
105
+ hoverDimsNonEndpoints: dimOk(during.gamma) && dimOk(during.delta),
106
+ releaseRestoresAll: baseOk(after.alpha) && baseOk(after.beta) && baseOk(after.gamma) && baseOk(after.delta),
107
+ };
108
+ const ok = Object.values(results).every(Boolean);
109
+ console.log(`${ok ? '✅' : '❌'} edge-endpoint highlight:`, JSON.stringify(results),
110
+ `\n before=`, before, `\n during=`, during, `\n after=`, after);
111
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,106 @@
1
+ /* Round 172 verification: edges fade-in alongside R9 staggered
2
+ * node reveal.
3
+ *
4
+ * Pre-R172 nodes used `anet-fade-in` with R9 stagger
5
+ * (animationDelay = tier×180 + nodeIdx×25 / nodeIdx×25 capped
6
+ * at 24) but edges appeared instantly. First-paint sequence:
7
+ * nodes ease in over ~600ms while edges already at full
8
+ * visibility — reveal looks inconsistent.
9
+ *
10
+ * R172 wraps each edge <g> in:
11
+ * className="anet-fade-in"
12
+ * style={{ animationDelay: '280ms + index*35ms (cap 20)' }}
13
+ * data-edge-group={link.key}
14
+ *
15
+ * Edges start fading in 280ms after first paint (after node
16
+ * stagger lands), staggered by 35ms intra-edge offset.
17
+ *
18
+ * Test:
19
+ * 1. Mock 4 sessions + 3 flows
20
+ * 2. Probe each edge <g[data-edge-group]>
21
+ * 3. Verify .anet-fade-in className present
22
+ * 4. Verify animation-delay attribute increments by 35ms
23
+ * starting from 280ms (clamped at idx=20)
24
+ */
25
+ import { chromium } from 'playwright';
26
+ import { readFileSync } from 'node:fs';
27
+
28
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
29
+ const browser = await chromium.launch({ headless: true });
30
+
31
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
32
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
33
+ await ctx.addInitScript(() => {
34
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
35
+ });
36
+
37
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
38
+ await ctx.route('**/api/hub/status*', async (route) => {
39
+ const r = await route.fetch();
40
+ const b = await r.json();
41
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
42
+ const mk = (alias) => ({
43
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
44
+ network_id: nid, project_dir: null,
45
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
46
+ });
47
+ await route.fulfill({ response: r, json: { ...b, sessions: [
48
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
49
+ ] } });
50
+ });
51
+
52
+ const now = Date.now();
53
+ // 3 distinct flow links so we can probe stagger across multiple indices
54
+ const msgs = [
55
+ { id: 'a', from_alias: 'alpha', to_alias: 'beta', content: 'hi', network_id: 'default', created_at: new Date(now - 5000).toISOString() },
56
+ { id: 'b', from_alias: 'beta', to_alias: 'gamma', content: 'hi', network_id: 'default', created_at: new Date(now - 10000).toISOString() },
57
+ { id: 'c', from_alias: 'gamma', to_alias: 'delta', content: 'hi', network_id: 'default', created_at: new Date(now - 15000).toISOString() },
58
+ ];
59
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
60
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
61
+
62
+ const page = await ctx.newPage();
63
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
64
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
65
+ await page.waitForSelector('[data-edge-group]', { timeout: 10000 });
66
+ await page.waitForTimeout(400);
67
+
68
+ const probe = await page.evaluate(() => {
69
+ const edges = [...document.querySelectorAll('[data-edge-group]')];
70
+ return edges.map(e => ({
71
+ key: e.getAttribute('data-edge-group'),
72
+ hasFadeClass: (e.getAttribute('class') || '').includes('anet-fade-in'),
73
+ animationDelay: e.style.animationDelay || getComputedStyle(e).animationDelay,
74
+ }));
75
+ });
76
+
77
+ await browser.close();
78
+
79
+ if (probe.length < 3) {
80
+ console.log(`❌ wrong edge count: expected 3+, got ${probe.length}`, probe);
81
+ process.exit(1);
82
+ }
83
+
84
+ // Parse animationDelay (e.g. "280ms" or "0.28s") → number in ms
85
+ const parseDelay = (s) => {
86
+ if (!s) return null;
87
+ if (s.endsWith('ms')) return parseFloat(s);
88
+ if (s.endsWith('s')) return parseFloat(s) * 1000;
89
+ return parseFloat(s);
90
+ };
91
+ const delays = probe.map(p => parseDelay(p.animationDelay));
92
+
93
+ const results = {
94
+ three_plus_edges: probe.length >= 3,
95
+ all_have_fadeClass: probe.every(p => p.hasFadeClass),
96
+ all_have_delay: delays.every(d => d !== null && d >= 280),
97
+ first_edge_280ms: Math.abs(delays[0] - 280) < 0.5,
98
+ second_edge_315ms: Math.abs(delays[1] - 315) < 0.5,
99
+ third_edge_350ms: Math.abs(delays[2] - 350) < 0.5,
100
+ delays_strictly_increase: delays[0] < delays[1] && delays[1] < delays[2],
101
+ };
102
+ const ok = Object.values(results).every(Boolean);
103
+ console.log(`${ok ? '✅' : '❌'} edge fade-in:`, JSON.stringify(results),
104
+ `\n edges =`, probe,
105
+ `\n delays (ms) =`, delays);
106
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,113 @@
1
+ /* Round 50 verification: hovering a flow edge boosts that edge's own
2
+ * opacity 2× and thickens its stroke 1.4×, while every other edge dims
3
+ * to 0.35×. Complements R49 (which highlights the endpoint nodes) —
4
+ * together they make "this is the edge you're pointing at" unmistakable.
5
+ *
6
+ * Sessions: alpha, beta, gamma, delta. Messages: alpha→beta + gamma→delta.
7
+ * Hover the alpha→beta hitbox and read:
8
+ * - alpha→beta visible-path opacity boosts (~2× base)
9
+ * - alpha→beta visible-path strokeWidth = 1.4 × base (rounded by browser)
10
+ * - gamma→delta visible-path opacity dims (~0.35× base)
11
+ * - gamma→delta visible-path strokeWidth unchanged
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const browser = await chromium.launch({ headless: true });
18
+
19
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
35
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ }));
38
+ await route.fulfill({ response: r, json: { ...b, sessions } });
39
+ });
40
+ const now = new Date().toISOString();
41
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
42
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
43
+ { from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
44
+ ] } }));
45
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
48
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
49
+ await page.waitForTimeout(600);
50
+
51
+ // Read each flow edge's visible base path (the one with marker-end).
52
+ const readEdges = () => page.evaluate(() => {
53
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
54
+ const rows = [];
55
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
56
+ const t = g.querySelector('path[data-edge-hitbox] title');
57
+ if (!t) continue;
58
+ const route = (t.textContent || '').split('\n')[0];
59
+ const base = [...g.querySelectorAll(':scope > path')].find(
60
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
61
+ );
62
+ if (!base) continue;
63
+ rows.push({
64
+ route,
65
+ opacity: +base.getAttribute('opacity'),
66
+ strokeWidth: +base.getAttribute('stroke-width'),
67
+ });
68
+ }
69
+ return rows;
70
+ });
71
+
72
+ const before = await readEdges();
73
+
74
+ // Move cursor to a point ON the alpha→beta hitbox curve.
75
+ const target = await page.evaluate(() => {
76
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
77
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
78
+ const t = g.querySelector('path[data-edge-hitbox] title');
79
+ if (t && /^alpha → beta/.test(t.textContent || '')) {
80
+ const hb = g.querySelector('path[data-edge-hitbox]');
81
+ const pt = hb.getPointAtLength(hb.getTotalLength() / 2);
82
+ const ctm = hb.getScreenCTM();
83
+ return { x: pt.x * ctm.a + pt.y * ctm.c + ctm.e, y: pt.x * ctm.b + pt.y * ctm.d + ctm.f };
84
+ }
85
+ }
86
+ return null;
87
+ });
88
+ if (!target) { console.log('❌ no alpha→beta hitbox found'); process.exit(1); }
89
+ await page.mouse.move(10, 10);
90
+ await page.mouse.move(target.x, target.y);
91
+ await page.waitForTimeout(350);
92
+ const during = await readEdges();
93
+
94
+ await browser.close();
95
+
96
+ const find = (rows, r) => rows.find(e => e.route === r);
97
+ const b_ab = find(before, 'alpha → beta');
98
+ const b_gd = find(before, 'gamma → delta');
99
+ const a_ab = find(during, 'alpha → beta');
100
+ const a_gd = find(during, 'gamma → delta');
101
+
102
+ const results = {
103
+ baselineMatches: b_ab && b_gd && Math.abs(b_ab.opacity - b_gd.opacity) < 0.001 && b_ab.strokeWidth === b_gd.strokeWidth,
104
+ hoveredEdgeBrightens: a_ab && a_ab.opacity > b_ab.opacity * 1.5,
105
+ hoveredEdgeThickens: a_ab && a_ab.strokeWidth > b_ab.strokeWidth * 1.2,
106
+ otherEdgeDims: a_gd && a_gd.opacity < b_gd.opacity * 0.6,
107
+ otherEdgeStaysThin: a_gd && a_gd.strokeWidth === b_gd.strokeWidth,
108
+ };
109
+ const ok = Object.values(results).every(Boolean);
110
+ console.log(`${ok ? '✅' : '❌'} edge focus:`, JSON.stringify(results),
111
+ `\n before α→β=${JSON.stringify(b_ab)} γ→δ=${JSON.stringify(b_gd)}`,
112
+ `\n during α→β=${JSON.stringify(a_ab)} γ→δ=${JSON.stringify(a_gd)}`);
113
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,93 @@
1
+ /* Round 40 verification: hovering a node brightens edges touching it
2
+ * and dims the rest. Three edges in the fleet — hover one node and
3
+ * compare base-path opacity of touching vs non-touching edges. */
4
+ import { chromium } from 'playwright';
5
+ import { readFileSync } from 'node:fs';
6
+
7
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
8
+ const browser = await chromium.launch({ headless: true });
9
+
10
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
11
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
12
+ await ctx.addInitScript(() => {
13
+ try {
14
+ localStorage.setItem('anet-theme', 'cyber');
15
+ localStorage.setItem('anet-topo-layout', 'grid');
16
+ sessionStorage.setItem('anet_v3_auth', '1');
17
+ } catch {}
18
+ });
19
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+ await ctx.route('**/api/hub/status*', async (route) => {
21
+ const r = await route.fetch();
22
+ const b = await r.json();
23
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
24
+ const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
25
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
26
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
27
+ }));
28
+ await route.fulfill({ response: r, json: { ...b, sessions } });
29
+ });
30
+ // Edges:
31
+ // alpha → beta (touches alpha + beta)
32
+ // alpha → gamma (touches alpha + gamma)
33
+ // delta → gamma (touches delta + gamma — does NOT touch alpha)
34
+ const now = new Date().toISOString();
35
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
36
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
37
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm', created_at: now },
38
+ { from_alias: 'delta', to_alias: 'gamma', content: 'm', created_at: now },
39
+ ] } }));
40
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
44
+ await page.waitForTimeout(600);
45
+
46
+ const edgeOpacities = () => page.evaluate(() => {
47
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
48
+ // R48 reordered the children of each flow-link <g>:
49
+ // path[data-edge-hitbox] <-- transparent hover surface, carries <title>
50
+ // path[marker-end] <-- the visible base edge with opacity
51
+ // path[id^=flow-path-] <-- the dashed runway
52
+ // Read the route from the hitbox's <title>, but the opacity from the
53
+ // visible base path. Picking by marker-end keeps the selector resilient
54
+ // to future siblings.
55
+ return [...svg.querySelectorAll(':scope > g > g')]
56
+ .map(g => {
57
+ const hitbox = g.querySelector('path[data-edge-hitbox]');
58
+ const title = hitbox?.querySelector('title');
59
+ const base = [...g.querySelectorAll(':scope > path')].find(
60
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
61
+ );
62
+ if (!title || !base) return null;
63
+ return { route: title.textContent.split('\n')[0], opacity: +base.getAttribute('opacity') };
64
+ })
65
+ .filter(Boolean);
66
+ });
67
+
68
+ const before = await edgeOpacities();
69
+ // Hover alpha → its two outgoing edges should brighten, delta→gamma should dim.
70
+ await page.locator('g[data-node="alpha"]').hover();
71
+ await page.waitForTimeout(350);
72
+ const after = await edgeOpacities();
73
+
74
+ await browser.close();
75
+ const get = (rows, r) => rows.find(e => e.route === r)?.opacity ?? null;
76
+ const b_ab = get(before, 'alpha → beta');
77
+ const b_ag = get(before, 'alpha → gamma');
78
+ const b_dg = get(before, 'delta → gamma');
79
+ const a_ab = get(after, 'alpha → beta');
80
+ const a_ag = get(after, 'alpha → gamma');
81
+ const a_dg = get(after, 'delta → gamma');
82
+
83
+ const results = {
84
+ baselineMatches: b_ab !== null && b_ag !== null && b_dg !== null && Math.abs(b_ab - b_dg) < 0.001,
85
+ hoverBrightensAlphaBeta: a_ab !== null && a_ab > b_ab + 0.05,
86
+ hoverBrightensAlphaGamma: a_ag !== null && a_ag > b_ag + 0.05,
87
+ hoverDimsDeltaGamma: a_dg !== null && a_dg < b_dg - 0.05,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} edge hover:`, JSON.stringify(results),
91
+ `\n before α→β=${b_ab} α→γ=${b_ag} δ→γ=${b_dg}`,
92
+ `\n after α→β=${a_ab} α→γ=${a_ag} δ→γ=${a_dg}`);
93
+ process.exit(ok ? 0 : 1);