@sleep2agi/agent-network-dashboard 0.4.6-preview.11 → 0.4.6-preview.111

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 (317) 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 +69 -55
  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/0052a9q2fvljq.js +1 -0
  181. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  182. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  183. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  184. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  185. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  186. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  187. package/.next/static/chunks/0ofu4mc55-oar.js +4 -0
  188. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  189. package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
  190. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  191. package/.next/static/chunks/115k17sotk9jg.css +2 -0
  192. package/.next/static/chunks/121d8yvfl8l_..js +7 -0
  193. package/.next/static/chunks/14ppi2e6ljtms.js +1 -0
  194. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  195. package/.next/trace +2 -2
  196. package/.next/trace-build +1 -1
  197. package/.next/types/routes.d.ts +2 -1
  198. package/.next/types/validator.ts +9 -0
  199. package/app/api/hub/servers/route.ts +39 -0
  200. package/app/components/AppShell.tsx +2 -0
  201. package/app/components/ChatPopover.tsx +38 -2
  202. package/app/components/CommandPalette.tsx +67 -0
  203. package/app/components/HelpOverlay.tsx +71 -0
  204. package/app/components/ServersDrawer.tsx +206 -0
  205. package/app/components/TopoGraph.tsx +2032 -157
  206. package/app/components/utils.ts +8 -7
  207. package/app/globals.css +19 -1
  208. package/app/lib/time.ts +36 -0
  209. package/package.json +1 -1
  210. package/scripts/help-overlay-topo-test.mjs +44 -0
  211. package/scripts/servers-drawer-live-test.mjs +96 -0
  212. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  213. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-arrival-ping-test.mjs +87 -0
  216. package/scripts/topo-arrow-tier-test.mjs +61 -0
  217. package/scripts/topo-auto-fit-test.mjs +73 -0
  218. package/scripts/topo-chat-focus-ring-test.mjs +77 -0
  219. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  220. package/scripts/topo-click-ripple-test.mjs +66 -0
  221. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  222. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  223. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  224. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  225. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  226. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  227. package/scripts/topo-edge-count-badge-test.mjs +84 -0
  228. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  229. package/scripts/topo-edge-focus-test.mjs +113 -0
  230. package/scripts/topo-edge-hover-test.mjs +93 -0
  231. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  232. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  233. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  234. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  235. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  236. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  237. package/scripts/topo-fit-key-test.mjs +106 -0
  238. package/scripts/topo-freshness-chip-test.mjs +74 -0
  239. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  240. package/scripts/topo-group-count-test.mjs +68 -0
  241. package/scripts/topo-group-hover-test.mjs +82 -0
  242. package/scripts/topo-group-label-hover-test.mjs +119 -0
  243. package/scripts/topo-group-label-title-test.mjs +83 -0
  244. package/scripts/topo-group-pin-test.mjs +119 -0
  245. package/scripts/topo-group-status-pips-test.mjs +103 -0
  246. package/scripts/topo-groupbox-live-test.mjs +100 -0
  247. package/scripts/topo-groupbox-test.mjs +4 -2
  248. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  249. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  250. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  251. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  252. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  253. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  254. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  255. package/scripts/topo-hub-busyness-test.mjs +100 -0
  256. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  257. package/scripts/topo-hub-title-test.mjs +93 -0
  258. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  259. package/scripts/topo-label-lift-test.mjs +119 -0
  260. package/scripts/topo-label-scale-test.mjs +78 -0
  261. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  262. package/scripts/topo-layout-key-test.mjs +91 -0
  263. package/scripts/topo-legend-count-test.mjs +88 -0
  264. package/scripts/topo-legend-hover-test.mjs +113 -0
  265. package/scripts/topo-legend-pin-test.mjs +128 -0
  266. package/scripts/topo-minimap-test.mjs +68 -0
  267. package/scripts/topo-mobile-chips-test.mjs +103 -0
  268. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  269. package/scripts/topo-node-title-test.mjs +101 -0
  270. package/scripts/topo-offline-dim-test.mjs +70 -0
  271. package/scripts/topo-overlap-test.mjs +32 -5
  272. package/scripts/topo-pan-cursor-test.mjs +62 -0
  273. package/scripts/topo-panel-elevation-test.mjs +76 -0
  274. package/scripts/topo-particle-stagger-test.mjs +82 -0
  275. package/scripts/topo-pin-persistence-test.mjs +105 -0
  276. package/scripts/topo-popover-meta-test.mjs +78 -0
  277. package/scripts/topo-pressure-bar-test.mjs +69 -0
  278. package/scripts/topo-pressure-hover-test.mjs +123 -0
  279. package/scripts/topo-pressure-pin-test.mjs +119 -0
  280. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  281. package/scripts/topo-pulse-rate-test.mjs +71 -0
  282. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  283. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  284. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  285. package/scripts/topo-recent-row-ts-test.mjs +92 -0
  286. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  287. package/scripts/topo-reduced-motion-test.mjs +77 -0
  288. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  289. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  290. package/scripts/topo-tier-ring-test.mjs +69 -0
  291. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  292. package/scripts/topo-tier-stagger-test.mjs +89 -0
  293. package/scripts/topo-tz-ghost-test.mjs +69 -0
  294. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  295. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  296. package/scripts/topo-vendor-pill-test.mjs +125 -0
  297. package/scripts/topo-vendor-pin-test.mjs +125 -0
  298. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  299. package/scripts/topo-zoom-keys-test.mjs +89 -0
  300. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  301. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  302. package/.next/static/chunks/03cra9~ibhxtq.js +0 -1
  303. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  304. package/.next/static/chunks/0cj5qr_a8dzky.js +0 -7
  305. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  306. package/.next/static/chunks/0g-urk~xe4p41.js +0 -1
  307. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  308. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  309. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  310. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  311. package/.next/static/chunks/0r~2i_zubnm0u.js +0 -1
  312. package/.next/static/chunks/0~5-jv77qynyc.css +0 -2
  313. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  314. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  315. /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_buildManifest.js +0 -0
  316. /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_clientMiddlewareManifest.js +0 -0
  317. /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_ssgManifest.js +0 -0
@@ -0,0 +1,133 @@
1
+ /* Round 90 verification: Cmd+K palette "Clear topology filters" also
2
+ * releases the R88 pinnedVendor (the R69 implementation only knew
3
+ * about status + group). Plus the new granular "Clear topology vendor
4
+ * filter" command releases just vendor without disturbing status or
5
+ * group pins.
6
+ *
7
+ * Drive via direct CustomEvent dispatch (avoids fuzzy-match flake —
8
+ * same approach as the R82 chip-pin-mirror test). Verifies both the
9
+ * TopoGraph listener branch (state mutation) and the in-memory
10
+ * sessionStorage cleanup.
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
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try {
21
+ localStorage.setItem('anet-theme', 'cyber');
22
+ localStorage.setItem('anet-topo-layout', 'ring');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ sessionStorage.removeItem('anet-topo-pinned-status');
25
+ sessionStorage.removeItem('anet-topo-pinned-group');
26
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
27
+ } catch {}
28
+ });
29
+
30
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
31
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const mk = (alias, status, model) => ({
36
+ alias, status, model, runtime: 'cli-claude-code',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ const sessions = [
41
+ mk('alpha', 'working', 'claude-opus-4'),
42
+ mk('beta', 'idle', 'gpt-5'),
43
+ mk('gamma', 'idle', 'claude-opus-4'),
44
+ ];
45
+ await route.fulfill({ response: r, json: { ...b, sessions } });
46
+ });
47
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
48
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const pills = () => page.evaluate(() => {
55
+ const all = document.querySelectorAll('[data-active-filter]');
56
+ return [...all].map(p => p.getAttribute('data-active-filter'));
57
+ });
58
+ const storage = () => page.evaluate(() => ({
59
+ status: sessionStorage.getItem('anet-topo-pinned-status'),
60
+ group: sessionStorage.getItem('anet-topo-pinned-group'),
61
+ vendor: sessionStorage.getItem('anet-topo-pinned-vendor'),
62
+ }));
63
+
64
+ // Helper: dispatch a topo-pin event from the page side.
65
+ const pin = async (kind, value) => {
66
+ await page.evaluate(({ kind, value }) => {
67
+ if (kind === 'status') sessionStorage.setItem('anet-topo-pinned-status', value);
68
+ if (kind === 'group') sessionStorage.setItem('anet-topo-pinned-group', value);
69
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind, value } }));
70
+ }, { kind, value });
71
+ await page.waitForTimeout(200);
72
+ };
73
+ // Pin vendor via the actual UI letter click (no CustomEvent for that
74
+ // flow yet — palette doesn't surface per-vendor pin actions).
75
+ const pinVendor = async (initial) => {
76
+ await page.locator(`[data-vendor-letter="${initial}"]`).click();
77
+ await page.mouse.move(10, 10);
78
+ await page.waitForTimeout(200);
79
+ };
80
+ const clearAll = async () => {
81
+ await page.evaluate(() => {
82
+ sessionStorage.removeItem('anet-topo-pinned-status');
83
+ sessionStorage.removeItem('anet-topo-pinned-group');
84
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
85
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear' } }));
86
+ });
87
+ await page.waitForTimeout(250);
88
+ };
89
+ const clearVendor = async () => {
90
+ await page.evaluate(() => {
91
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
92
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear-vendor' } }));
93
+ });
94
+ await page.waitForTimeout(250);
95
+ };
96
+
97
+ // === Test 1: clear-all now also clears vendor (the R90 bug fix). ===
98
+ await pin('status', 'working');
99
+ await pinVendor('A');
100
+ const afterPinBoth = { pills: await pills(), storage: await storage() };
101
+
102
+ await clearAll();
103
+ const afterClearAll = { pills: await pills(), storage: await storage() };
104
+
105
+ // === Test 2: granular clear-vendor leaves status + group untouched. ===
106
+ await pin('status', 'working');
107
+ await pinVendor('A');
108
+ const beforeGranular = { pills: await pills(), storage: await storage() };
109
+
110
+ await clearVendor();
111
+ const afterClearVendor = { pills: await pills(), storage: await storage() };
112
+
113
+ await browser.close();
114
+
115
+ const sameSet = (a, b) => a.length === b.length && a.every(x => b.includes(x));
116
+ const results = {
117
+ pinBoth_threePills: sameSet(afterPinBoth.pills, ['status', 'vendor']),
118
+ pinBoth_storageBoth: afterPinBoth.storage.status === 'working' && afterPinBoth.storage.vendor === 'A',
119
+ clearAll_noPills: afterClearAll.pills.length === 0,
120
+ clearAll_storageAllNull: afterClearAll.storage.status === null && afterClearAll.storage.group === null && afterClearAll.storage.vendor === null,
121
+ granular_threePills: sameSet(beforeGranular.pills, ['status', 'vendor']),
122
+ clearVendor_keepsStatus: afterClearVendor.pills.includes('status'),
123
+ clearVendor_dropsVendor: !afterClearVendor.pills.includes('vendor'),
124
+ clearVendor_storageStatusKept: afterClearVendor.storage.status === 'working',
125
+ clearVendor_storageVendorCleared: afterClearVendor.storage.vendor === null,
126
+ };
127
+ const ok = Object.values(results).every(Boolean);
128
+ console.log(`${ok ? '✅' : '❌'} cmdk vendor clear:`, JSON.stringify(results),
129
+ `\n afterPinBoth=`, afterPinBoth,
130
+ `\n afterClearAll=`, afterClearAll,
131
+ `\n beforeGranular=`, beforeGranular,
132
+ `\n afterClearVendor=`, afterClearVendor);
133
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,64 @@
1
+ /* Round 33 verification: hover tooltip carries `cwd: <project_dir>` on a
2
+ * second line when reported; falls back to the identity-only line
3
+ * otherwise; no `cwd:` line for nodes without project_dir. */
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
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
10
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
11
+ await ctx.addInitScript(() => {
12
+ try {
13
+ localStorage.setItem('anet-theme', 'cyber');
14
+ localStorage.setItem('anet-topo-layout', 'grid');
15
+ sessionStorage.setItem('anet_v3_auth', '1');
16
+ } catch {}
17
+ });
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+ await ctx.route('**/api/hub/status*', async (route) => {
20
+ const r = await route.fetch();
21
+ const b = await r.json();
22
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
23
+ const sessions = [
24
+ {
25
+ alias: 'full', status: 'idle', network_id: nid,
26
+ project_dir: '/home/vansin/agent-orchestra',
27
+ model: 'claude-opus-4-7', runtime: 'cli-claude-code',
28
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
29
+ },
30
+ {
31
+ alias: 'cwdOnly', status: 'idle', network_id: nid,
32
+ project_dir: '/home/vansin/some-project',
33
+ model: null, runtime: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ },
36
+ {
37
+ alias: 'bare', status: 'idle', network_id: nid,
38
+ project_dir: null, model: null, runtime: null,
39
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
40
+ },
41
+ ];
42
+ await route.fulfill({ response: r, json: { ...b, sessions } });
43
+ });
44
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
47
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
48
+ await page.waitForTimeout(400);
49
+
50
+ const titleOf = alias => page.$eval(`g[data-node="${alias}"] title`, el => el.textContent);
51
+ const full = await titleOf('full');
52
+ const cwdOnly = await titleOf('cwdOnly');
53
+ const bare = await titleOf('bare');
54
+
55
+ await browser.close();
56
+ const results = {
57
+ fullHasIdentityAndCwd: /claude-opus-4-7/.test(full) && /cwd: \/home\/vansin\/agent-orchestra/.test(full) && full.includes('\n'),
58
+ cwdOnlyShowsAliasAndCwd: cwdOnly.startsWith('cwdOnly') && /cwd: \/home\/vansin\/some-project/.test(cwdOnly),
59
+ bareShowsAliasOnly: bare === 'bare',
60
+ };
61
+ const ok = Object.values(results).every(Boolean);
62
+ console.log(`${ok ? '✅' : '❌'} cwd tooltip:`, JSON.stringify(results),
63
+ `\n full=${JSON.stringify(full)}\n cwdOnly=${JSON.stringify(cwdOnly)}\n bare=${JSON.stringify(bare)}`);
64
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,74 @@
1
+ /* Round 41 verification: double-clicking empty canvas resets view to
2
+ * {zoom:1, x:0, y:0}. Double-clicking a node does NOT reset (the
3
+ * node's own onClick wins, and resetView should not fire). */
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
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
10
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
11
+ await ctx.addInitScript(() => {
12
+ try {
13
+ localStorage.setItem('anet-theme', 'cyber');
14
+ localStorage.setItem('anet-topo-layout', 'grid');
15
+ localStorage.setItem('anet-topo-view', JSON.stringify({ zoom: 1, x: 0, y: 0 }));
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 = ['a', 'b'].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
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
31
+ const page = await ctx.newPage();
32
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
33
+ await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
34
+ await page.waitForTimeout(500);
35
+
36
+ const readZoom = () => page.evaluate(() => {
37
+ const span = document.querySelector('button[aria-label="Zoom in"]').parentElement.querySelector('span[title*="zoom level"]');
38
+ return +span.textContent.replace('%', '');
39
+ });
40
+
41
+ // Zoom in a couple times via keyboard, then dbl-click empty canvas → expect reset to 100%.
42
+ await page.keyboard.press('=');
43
+ await page.keyboard.press('=');
44
+ await page.waitForTimeout(120);
45
+ const zoomedIn = await readZoom();
46
+
47
+ // Find empty-canvas coords: top-left area of the SVG, well outside node positions.
48
+ const box = await page.$eval('svg[viewBox="0 0 1000 680"]', el => el.getBoundingClientRect());
49
+ // y=0.35 is below the top-overlay panels (which sit at y<100 inside the SVG)
50
+ const emptyX = box.x + box.width * 0.20;
51
+ const emptyY = box.y + box.height * 0.40;
52
+ await page.mouse.dblclick(emptyX, emptyY);
53
+ await page.waitForTimeout(200);
54
+ const afterEmptyDbl = await readZoom();
55
+
56
+ // Now zoom in again and dbl-click a node — view should NOT reset.
57
+ await page.keyboard.press('=');
58
+ await page.keyboard.press('=');
59
+ await page.waitForTimeout(120);
60
+ const zoomedIn2 = await readZoom();
61
+ await page.locator('g[data-node="a"]').dblclick().catch(() => {});
62
+ await page.waitForTimeout(200);
63
+ const afterNodeDbl = await readZoom();
64
+
65
+ await browser.close();
66
+ const results = {
67
+ zoomedInBeforeDbl: zoomedIn > 100,
68
+ emptyCanvasResets: afterEmptyDbl === 100,
69
+ nodeDoesNotReset: zoomedIn2 > 100 && afterNodeDbl === zoomedIn2,
70
+ };
71
+ const ok = Object.values(results).every(Boolean);
72
+ console.log(`${ok ? '✅' : '❌'} dblclick reset:`, JSON.stringify(results),
73
+ `\n zoomedIn=${zoomedIn} afterEmptyDbl=${afterEmptyDbl} zoomedIn2=${zoomedIn2} afterNodeDbl=${afterNodeDbl}`);
74
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,83 @@
1
+ /* Round 76 verification: source-side dispatch pulse renders for high-
2
+ * traffic edges only (link.count >= 3), bookending the R75 destination
3
+ * ping. Quiet edges (count < 3) skip the source pulse to keep the
4
+ * canvas calm. Reduced-motion users see neither pulse nor ping.
5
+ *
6
+ * - Busy alpha→beta (5 messages, fresh) → dispatch pulse present at
7
+ * source AND arrival ping at destination.
8
+ * - Quiet gamma→delta (1 message, fresh) → arrival ping only, no
9
+ * dispatch pulse.
10
+ * - Reduced-motion: no dispatch pulses, no arrival pings.
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
+ async function probe(reducedMotion) {
19
+ const ctx = await browser.newContext({
20
+ viewport: { width: 1280, height: 1500 },
21
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
22
+ });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try {
26
+ localStorage.setItem('anet-theme', 'cyber');
27
+ localStorage.setItem('anet-topo-layout', 'ring');
28
+ sessionStorage.setItem('anet_v3_auth', '1');
29
+ sessionStorage.removeItem('anet-topo-pinned-status');
30
+ sessionStorage.removeItem('anet-topo-pinned-group');
31
+ } catch {}
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
+ // 5 busy messages alpha→beta + 1 quiet gamma→delta.
45
+ const now = new Date().toISOString();
46
+ const msgs = [
47
+ ...Array.from({ length: 5 }).map(() => ({ from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now })),
48
+ { from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
49
+ ];
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
51
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
52
+ const page = await ctx.newPage();
53
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
54
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
55
+ await page.waitForTimeout(600);
56
+ const elements = await page.evaluate(() => {
57
+ return {
58
+ dispatchPulses: [...document.querySelectorAll('[data-dispatch-pulse]')].map(el => el.getAttribute('data-dispatch-pulse')),
59
+ arrivalPings: [...document.querySelectorAll('[data-arrival-ping]')].map(el => el.getAttribute('data-arrival-ping')),
60
+ };
61
+ });
62
+ await ctx.close();
63
+ return elements;
64
+ }
65
+
66
+ const normal = await probe(false);
67
+ const reduced = await probe(true);
68
+ await browser.close();
69
+
70
+ const has = (arr, key) => arr.includes(key);
71
+ const results = {
72
+ normal_busyHasDispatch: has(normal.dispatchPulses, 'alpha->beta'),
73
+ normal_quietHasNoDispatch: !has(normal.dispatchPulses, 'gamma->delta'),
74
+ normal_busyHasArrival: has(normal.arrivalPings, 'alpha->beta'),
75
+ normal_quietHasArrival: has(normal.arrivalPings, 'gamma->delta'),
76
+ reduced_noDispatchPulses: reduced.dispatchPulses.length === 0,
77
+ reduced_noArrivalPings: reduced.arrivalPings.length === 0,
78
+ };
79
+ const ok = Object.values(results).every(Boolean);
80
+ console.log(`${ok ? '✅' : '❌'} dispatch pulse:`, JSON.stringify(results),
81
+ `\n normal=`, normal,
82
+ `\n reduced=`, reduced);
83
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,84 @@
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
+ badges_nonInteractive: badges.every(b => b.pe === 'none'),
80
+ };
81
+ const ok = Object.values(results).every(Boolean);
82
+ console.log(`${ok ? '✅' : '❌'} edge-count badge:`, JSON.stringify(results),
83
+ `\n badges=`, badges);
84
+ 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,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);