@sleep2agi/agent-network-dashboard 0.4.6-preview.17 → 0.4.6-preview.170

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 (374) 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/050s9ndkk07i9.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/0d7u.7_u.l9sh.js +4 -0
  187. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  188. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  189. package/.next/static/chunks/0gvra4akqlit0.js +1 -0
  190. package/.next/static/chunks/0otgjhpse~amj.css +2 -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 +3338 -241
  204. package/app/components/utils.ts +8 -7
  205. package/app/globals.css +75 -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-focus-ring-test.mjs +77 -0
  221. package/scripts/topo-chat-target-breath-test.mjs +84 -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-dispatch-pulse-test.mjs +83 -0
  234. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  235. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  236. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  237. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  238. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  239. package/scripts/topo-edge-focus-test.mjs +113 -0
  240. package/scripts/topo-edge-hover-test.mjs +93 -0
  241. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  242. package/scripts/topo-edge-pill-test.mjs +105 -0
  243. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  244. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  245. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  246. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  247. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  248. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  249. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  250. package/scripts/topo-fit-key-test.mjs +106 -0
  251. package/scripts/topo-freshness-chip-test.mjs +74 -0
  252. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  253. package/scripts/topo-group-count-test.mjs +68 -0
  254. package/scripts/topo-group-hover-test.mjs +82 -0
  255. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  256. package/scripts/topo-group-label-hover-test.mjs +119 -0
  257. package/scripts/topo-group-label-tint-test.mjs +99 -0
  258. package/scripts/topo-group-label-title-test.mjs +83 -0
  259. package/scripts/topo-group-pin-test.mjs +119 -0
  260. package/scripts/topo-group-status-pips-test.mjs +103 -0
  261. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  262. package/scripts/topo-groupbox-live-test.mjs +100 -0
  263. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  264. package/scripts/topo-groupbox-test.mjs +4 -2
  265. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  266. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  267. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  268. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  269. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  270. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  271. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  272. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  273. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  274. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  275. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  276. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  277. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  278. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  279. package/scripts/topo-hub-a11y-test.mjs +112 -0
  280. package/scripts/topo-hub-busyness-test.mjs +100 -0
  281. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  282. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  283. package/scripts/topo-hub-title-test.mjs +93 -0
  284. package/scripts/topo-hub-working-count-test.mjs +99 -0
  285. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  286. package/scripts/topo-label-lift-test.mjs +119 -0
  287. package/scripts/topo-label-scale-test.mjs +78 -0
  288. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  289. package/scripts/topo-layout-key-test.mjs +91 -0
  290. package/scripts/topo-layout-toggle-test.mjs +131 -0
  291. package/scripts/topo-legend-count-test.mjs +88 -0
  292. package/scripts/topo-legend-hover-test.mjs +113 -0
  293. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  294. package/scripts/topo-legend-pin-test.mjs +128 -0
  295. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  296. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  297. package/scripts/topo-legend-row-title-test.mjs +102 -0
  298. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  299. package/scripts/topo-minimap-test.mjs +68 -0
  300. package/scripts/topo-mobile-chips-test.mjs +103 -0
  301. package/scripts/topo-node-a11y-test.mjs +107 -0
  302. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  303. package/scripts/topo-node-title-peers-test.mjs +107 -0
  304. package/scripts/topo-node-title-test.mjs +101 -0
  305. package/scripts/topo-offline-dim-test.mjs +70 -0
  306. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  307. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  308. package/scripts/topo-overlap-test.mjs +32 -5
  309. package/scripts/topo-pan-cursor-test.mjs +62 -0
  310. package/scripts/topo-panel-elevation-test.mjs +76 -0
  311. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  312. package/scripts/topo-particle-stagger-test.mjs +82 -0
  313. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  314. package/scripts/topo-pin-intersection-test.mjs +121 -0
  315. package/scripts/topo-pin-persistence-test.mjs +105 -0
  316. package/scripts/topo-popover-meta-test.mjs +78 -0
  317. package/scripts/topo-pressure-bar-test.mjs +69 -0
  318. package/scripts/topo-pressure-hover-test.mjs +123 -0
  319. package/scripts/topo-pressure-pin-test.mjs +119 -0
  320. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  321. package/scripts/topo-pulse-rate-test.mjs +71 -0
  322. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  323. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  324. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  325. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  326. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  327. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  328. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  329. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  330. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  331. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  332. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  333. package/scripts/topo-recent-row-title-test.mjs +112 -0
  334. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  335. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  336. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  337. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  338. package/scripts/topo-reduced-motion-test.mjs +77 -0
  339. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  340. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  341. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  342. package/scripts/topo-svg-focus-test.mjs +112 -0
  343. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  344. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  345. package/scripts/topo-tier-ring-test.mjs +69 -0
  346. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  347. package/scripts/topo-tier-stagger-test.mjs +89 -0
  348. package/scripts/topo-tz-ghost-test.mjs +69 -0
  349. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  350. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  351. package/scripts/topo-vendor-pill-test.mjs +125 -0
  352. package/scripts/topo-vendor-pin-test.mjs +125 -0
  353. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  354. package/scripts/topo-working-chip-pin-test.mjs +148 -0
  355. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  356. package/scripts/topo-zoom-keys-test.mjs +89 -0
  357. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  358. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  359. package/.next/static/chunks/096hkjkwj4hb3.js +0 -1
  360. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  361. package/.next/static/chunks/0d6xn54cd2zo-.js +0 -1
  362. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  363. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  364. package/.next/static/chunks/0iv2-b3po~sbg.js +0 -1
  365. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  366. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  367. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  368. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  369. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  370. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  371. package/.next/static/chunks/13vw-ubg-9pjp.css +0 -2
  372. /package/.next/static/{TBtREVf7o4zYSqGqq3QT5 → m3FtdjT1mdUIOaZp0o9gq}/_buildManifest.js +0 -0
  373. /package/.next/static/{TBtREVf7o4zYSqGqq3QT5 → m3FtdjT1mdUIOaZp0o9gq}/_clientMiddlewareManifest.js +0 -0
  374. /package/.next/static/{TBtREVf7o4zYSqGqq3QT5 → m3FtdjT1mdUIOaZp0o9gq}/_ssgManifest.js +0 -0
@@ -0,0 +1,101 @@
1
+ /* Round 157 verification: minimap a11y completion.
2
+ *
3
+ * Pre-R157 the R30 minimap had role="img" (wrong — it's clickable)
4
+ * + aria-label but no tabIndex / onKeyDown. Mouse users could
5
+ * click to recenter; keyboard-only users couldn't reach or
6
+ * activate it.
7
+ *
8
+ * R157 closes the gap with the standard pattern (R116/R139/R140/
9
+ * R151/R152):
10
+ * role="button"
11
+ * tabIndex={0}
12
+ * aria-label="Topology minimap — click to recenter, Enter to reset view"
13
+ * onKeyDown(Enter/Space) → resetView()
14
+ * anet-topo-chip-focus className (R155 cyan outline)
15
+ *
16
+ * Test:
17
+ * 1. Force the canvas off-default via zoom (so minimap renders)
18
+ * 2. Verify role / tabIndex / aria-label / class on the minimap
19
+ * 3. Press Enter on focused minimap → view resets (zoom back to 100%)
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const browser = await chromium.launch({ headless: true });
26
+
27
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
28
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
29
+ await ctx.addInitScript(() => {
30
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
31
+ });
32
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
33
+ await ctx.route('**/api/hub/status*', async (route) => {
34
+ const r = await route.fetch();
35
+ const b = await r.json();
36
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
37
+ const mk = (alias) => ({
38
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
39
+ 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: [mk('alpha'), mk('beta')] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
50
+ await page.waitForTimeout(400);
51
+
52
+ // Initial: zoom is 100%, minimap not rendered (gated to non-default view)
53
+ const minimapBefore = await page.locator('[data-topo-minimap]').count();
54
+
55
+ // Press + (zoom in) twice to render the minimap
56
+ await page.keyboard.press('+');
57
+ await page.keyboard.press('+');
58
+ await page.waitForTimeout(250);
59
+
60
+ const probe = await page.evaluate(() => {
61
+ const el = document.querySelector('[data-topo-minimap]');
62
+ if (!el) return null;
63
+ return {
64
+ role: el.getAttribute('role'),
65
+ tabIndex: el.getAttribute('tabindex'),
66
+ ariaLabel: el.getAttribute('aria-label'),
67
+ title: el.getAttribute('title'),
68
+ className: el.getAttribute('class') || '',
69
+ zoomLevel: document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim(),
70
+ };
71
+ });
72
+
73
+ // Focus minimap + Enter → reset view (zoom back to 100%)
74
+ await page.locator('[data-topo-minimap]').focus();
75
+ await page.keyboard.press('Enter');
76
+ await page.waitForTimeout(250);
77
+ const afterEnter = await page.evaluate(() => ({
78
+ zoomLevel: document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim(),
79
+ minimapHidden: !document.querySelector('[data-topo-minimap]'),
80
+ }));
81
+
82
+ await browser.close();
83
+
84
+ const results = {
85
+ minimap_hiddenAtDefault: minimapBefore === 0,
86
+ minimap_renderedAfterZoom: probe !== null,
87
+ role_button: probe?.role === 'button',
88
+ tabIndex_0: probe?.tabIndex === '0',
89
+ ariaLabel_mentionsEnter: (probe?.ariaLabel || '').includes('Enter'),
90
+ title_mentionsEnter: (probe?.title || '').includes('Enter'),
91
+ hasFocusClass: (probe?.className || '').includes('anet-topo-chip-focus'),
92
+ zoomedBeforeReset: probe?.zoomLevel !== '100%',
93
+ enter_resetsZoom: afterEnter.zoomLevel === '100%',
94
+ enter_hidesMinimap: afterEnter.minimapHidden === true,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} minimap a11y:`, JSON.stringify(results),
98
+ `\n before count=${minimapBefore}`,
99
+ `\n probe=`, probe,
100
+ `\n afterEnter=`, afterEnter);
101
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,68 @@
1
+ /* Round 30 verification: minimap.
2
+ * - not mounted at default view (1×, centered)
3
+ * - mounted when zoomed in (one node dot per session, viewport rect)
4
+ * - click recenters the canvas (view.x / view.y change) */
5
+ import { chromium } from 'playwright';
6
+ import { readFileSync } from 'node:fs';
7
+
8
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
9
+ const browser = await chromium.launch({ headless: true });
10
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
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
+ localStorage.setItem('anet-topo-view', JSON.stringify({ zoom: 1, x: 0, y: 0 }));
17
+ sessionStorage.setItem('anet_v3_auth', '1');
18
+ } catch {}
19
+ });
20
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
21
+ await ctx.route('**/api/hub/status*', async (route) => {
22
+ const r = await route.fetch();
23
+ const b = await r.json();
24
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
25
+ const sessions = ['A站', 'B站', 'C站', 'D站', 'E站', 'F站'].flatMap(g =>
26
+ Array.from({ length: 3 }, (_, i) => ({
27
+ alias: `${g}${i + 1}`, status: 'idle', network_id: nid, project_dir: null,
28
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
29
+ })),
30
+ );
31
+ await route.fulfill({ response: r, json: { ...b, sessions } });
32
+ });
33
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
34
+ const page = await ctx.newPage();
35
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
36
+ await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
37
+ await page.waitForTimeout(500);
38
+
39
+ const minimap = '[data-topo-minimap]';
40
+ const notMountedAtDefault = (await page.locator(minimap).count()) === 0;
41
+
42
+ // Zoom in via `+` twice → view changes → minimap mounts
43
+ await page.keyboard.press('=');
44
+ await page.keyboard.press('=');
45
+ await page.waitForTimeout(200);
46
+ const mountedAfterZoom = (await page.locator(minimap).count()) === 1;
47
+ const dotCount = await page.locator(`${minimap} svg circle`).count();
48
+ const hasViewportRect = await page.locator(`${minimap} svg rect`).count() > 0;
49
+
50
+ // Click on the minimap's far left → expect view.x to become more positive (canvas pans right)
51
+ const beforeView = await page.evaluate(() => JSON.parse(localStorage.getItem('anet-topo-view')));
52
+ const mmBox = await page.locator(minimap).boundingBox();
53
+ await page.mouse.click(mmBox.x + 10, mmBox.y + mmBox.height / 2);
54
+ await page.waitForTimeout(200);
55
+ const afterView = await page.evaluate(() => JSON.parse(localStorage.getItem('anet-topo-view')));
56
+ const clickRecenters = afterView.x !== beforeView.x || afterView.y !== beforeView.y;
57
+
58
+ await browser.close();
59
+ const results = {
60
+ notMountedAtDefault,
61
+ mountedAfterZoom,
62
+ oneDotPerSession: dotCount === 18, // 6 groups × 3 = 18 nodes
63
+ viewportRectPresent: hasViewportRect,
64
+ clickRecenters,
65
+ };
66
+ const ok = Object.values(results).every(Boolean);
67
+ console.log(`${ok ? '✅' : '❌'} minimap:`, JSON.stringify(results), `dots=${dotCount} view before=`, beforeView, `after=`, afterView);
68
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 47 verification: mobile chip collapse.
2
+ * - At narrow viewport (375px), pressure / vendor-dist / active-links /
3
+ * freshness chips are display:none; only essential chips render.
4
+ * - At sm+ (≥640px), the full chip row is visible.
5
+ * - Essential chips (Ring/Grid toggle + working + online) render at both. */
6
+ import { chromium } from 'playwright';
7
+ import { readFileSync } from 'node:fs';
8
+
9
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
10
+ const browser = await chromium.launch({ headless: true });
11
+
12
+ async function probe(width) {
13
+ const ctx = await browser.newContext({ viewport: { width, height: 900 } });
14
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
15
+ await ctx.addInitScript(() => {
16
+ try {
17
+ localStorage.setItem('anet-theme', 'cyber');
18
+ localStorage.setItem('anet-topo-layout', 'grid');
19
+ sessionStorage.setItem('anet_v3_auth', '1');
20
+ } catch {}
21
+ });
22
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
23
+ await ctx.route('**/api/hub/status*', async (route) => {
24
+ const r = await route.fetch();
25
+ const b = await r.json();
26
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
27
+ const sessions = [
28
+ { alias: 'wkr', status: 'working', network_id: nid, project_dir: null,
29
+ model: 'claude-opus-4', runtime: 'cli-claude-code',
30
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh },
31
+ { alias: 'idl', status: 'idle', network_id: nid, project_dir: null,
32
+ model: 'gpt-5', runtime: 'codex-cli',
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh },
34
+ ];
35
+ await route.fulfill({ response: r, json: { ...b, sessions } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
38
+ { from_alias: 'wkr', to_alias: 'idl', content: 'm', created_at: new Date().toISOString() },
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
+ // The Overview page auto-hides the topology when window.innerWidth < 1024
44
+ // (the lg breakpoint). On narrow viewports the user clicks "Show
45
+ // Topology" to surface it; mimic that here so the chip-row tests reach
46
+ // the actual code path. Wait for SWR to deliver sessions first so the
47
+ // button renders before we look for it.
48
+ await page.waitForTimeout(1500);
49
+ const showBtn = page.locator('button', { hasText: /Show Topology/i });
50
+ if (await showBtn.count() > 0) await showBtn.first().click();
51
+ await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
52
+ await page.waitForTimeout(600);
53
+ const visible = (sel) => page.evaluate(s => {
54
+ const el = document.querySelector(s);
55
+ if (!el) return null;
56
+ const r = el.getBoundingClientRect();
57
+ return r.width > 0 && r.height > 0;
58
+ }, sel);
59
+ const result = {
60
+ workingChipVisible: await page.evaluate(() => {
61
+ // "{N} working" chip — find the green-tinted one
62
+ const spans = [...document.querySelectorAll('span')];
63
+ const chip = spans.find(s => /^\d+ working$/.test((s.textContent || '').trim()));
64
+ if (!chip) return false;
65
+ const r = chip.getBoundingClientRect();
66
+ return r.width > 0 && r.height > 0;
67
+ }),
68
+ onlineChipVisible: await page.evaluate(() => {
69
+ const spans = [...document.querySelectorAll('span')];
70
+ const chip = spans.find(s => /^\d+ online$/.test((s.textContent || '').trim()));
71
+ if (!chip) return false;
72
+ const r = chip.getBoundingClientRect();
73
+ return r.width > 0 && r.height > 0;
74
+ }),
75
+ pressureVisible: await visible('[data-fleet-pressure]'),
76
+ activeLinksVisible: await page.evaluate(() => {
77
+ const spans = [...document.querySelectorAll('span')];
78
+ const chip = spans.find(s => /active link/.test(s.textContent || ''));
79
+ if (!chip) return false;
80
+ const r = chip.getBoundingClientRect();
81
+ return r.width > 0 && r.height > 0;
82
+ }),
83
+ freshnessVisible: await visible('[data-freshness-chip]'),
84
+ };
85
+ await ctx.close();
86
+ return result;
87
+ }
88
+
89
+ const mobile = await probe(375);
90
+ const desktop = await probe(900);
91
+ await browser.close();
92
+
93
+ const results = {
94
+ mobileEssentialsVisible: mobile.workingChipVisible && mobile.onlineChipVisible,
95
+ mobilePressureHidden: mobile.pressureVisible === false,
96
+ mobileActiveLinksHidden: mobile.activeLinksVisible === false,
97
+ mobileFreshnessHidden: mobile.freshnessVisible === false,
98
+ desktopAllVisible: desktop.workingChipVisible && desktop.onlineChipVisible && desktop.pressureVisible && desktop.activeLinksVisible && desktop.freshnessVisible,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} mobile chips:`, JSON.stringify(results),
102
+ `\n mobile=`, mobile, `\n desktop=`, desktop);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,107 @@
1
+ /* Round 151 verification: node <g> gains a11y compliance.
2
+ *
3
+ * Node clickable for chat since R45, but tab-unreachable +
4
+ * screen-reader-unannounced. R151 matches the pattern R116 /
5
+ * R139 / R140 / R148 / R149 applied to other interactive
6
+ * surfaces:
7
+ * role="button"
8
+ * tabIndex={0}
9
+ * aria-label="Chat with {alias} ({status})"
10
+ * aria-pressed={chatAlias === session.alias}
11
+ * onKeyDown(Enter / Space) → setChatAlias
12
+ *
13
+ * Three checks:
14
+ * 1. All nodes carry role=button, tabIndex=0, aria-label, aria-
15
+ * pressed=false by default
16
+ * 2. Pressing Enter on a focused node opens chat (aria-pressed
17
+ * flips to true, chat popover renders)
18
+ * 3. Pressing Space on a different node moves the open chat
19
+ * target — only one aria-pressed=true at a time
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const browser = await chromium.launch({ headless: true });
26
+
27
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
28
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
29
+ await ctx.addInitScript(() => {
30
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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 mk = (alias, status) => ({
39
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
40
+ network_id: nid, project_dir: null,
41
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
42
+ });
43
+ await route.fulfill({ response: r, json: { ...b, sessions: [
44
+ mk('alpha', 'working'), mk('beta', 'idle'), mk('gamma', 'working'),
45
+ ] } });
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
+
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 === 3, { timeout: 30000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ const inspectAll = () => page.evaluate(() => {
56
+ const nodes = [...document.querySelectorAll('g[data-node]')];
57
+ return nodes.map(n => ({
58
+ alias: n.getAttribute('data-node'),
59
+ role: n.getAttribute('role'),
60
+ tabIndex: n.getAttribute('tabindex'),
61
+ ariaLabel: n.getAttribute('aria-label'),
62
+ ariaPressed: n.getAttribute('aria-pressed'),
63
+ }));
64
+ });
65
+
66
+ const before = await inspectAll();
67
+
68
+ // Focus alpha node and press Enter → opens chat
69
+ await page.locator('g[data-node="alpha"]').focus();
70
+ await page.keyboard.press('Enter');
71
+ await page.waitForTimeout(250);
72
+ const afterAlpha = await inspectAll();
73
+ const popoverPresentA = await page.locator('.anet-chat-popover, [data-chat-popover]').count() > 0
74
+ || await page.evaluate(() => !!document.querySelector('[role="dialog"]'));
75
+
76
+ // Focus gamma node and press Space → moves chat target (only one pressed)
77
+ await page.locator('g[data-node="gamma"]').focus();
78
+ await page.keyboard.press(' ');
79
+ await page.waitForTimeout(250);
80
+ const afterGamma = await inspectAll();
81
+
82
+ await browser.close();
83
+
84
+ const results = {
85
+ // Baseline shape
86
+ threeNodes: before.length === 3,
87
+ allRoleButton: before.every(n => n.role === 'button'),
88
+ allTabIndex0: before.every(n => n.tabIndex === '0'),
89
+ allAriaLabel: before.every(n => /^Chat with /.test(n.ariaLabel || '')),
90
+ allAriaPressedFalse: before.every(n => n.ariaPressed === 'false'),
91
+
92
+ // After Enter on alpha
93
+ enterOpensChat: afterAlpha.find(n => n.alias === 'alpha')?.ariaPressed === 'true',
94
+ popoverShown: popoverPresentA,
95
+ othersStillFalse: afterAlpha.filter(n => n.alias !== 'alpha').every(n => n.ariaPressed === 'false'),
96
+
97
+ // After Space on gamma (chat target moved)
98
+ spaceMovesTarget: afterGamma.find(n => n.alias === 'gamma')?.ariaPressed === 'true',
99
+ alphaUnpressed: afterGamma.find(n => n.alias === 'alpha')?.ariaPressed === 'false',
100
+ onlyOnePressed: afterGamma.filter(n => n.ariaPressed === 'true').length === 1,
101
+ };
102
+ const ok = Object.values(results).every(Boolean);
103
+ console.log(`${ok ? '✅' : '❌'} node a11y:`, JSON.stringify(results),
104
+ `\n before=`, before,
105
+ `\n afterAlpha=`, afterAlpha,
106
+ `\n afterGamma=`, afterGamma);
107
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,85 @@
1
+ /* Round 51 verification: hovered node <g> lifts -2px via CSS transform.
2
+ * - At rest, every node's CSS transform is "none" (or matrix with 0 ty).
3
+ * - On hover, the hovered <g> reports `translateY(-2px)` (matrix(1,0,0,1,0,-2))
4
+ * via getComputedStyle.transform, while siblings stay at identity.
5
+ * - Release: returns to identity.
6
+ * - With prefers-reduced-motion forced, NO lift even on hover.
7
+ */
8
+ import { chromium } from 'playwright';
9
+ import { readFileSync } from 'node:fs';
10
+
11
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
12
+ const browser = await chromium.launch({ headless: true });
13
+
14
+ async function probe(reducedMotion) {
15
+ const ctx = await browser.newContext({
16
+ viewport: { width: 1280, height: 900 },
17
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
18
+ });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try {
22
+ localStorage.setItem('anet-theme', 'cyber');
23
+ localStorage.setItem('anet-topo-layout', 'ring');
24
+ sessionStorage.setItem('anet_v3_auth', '1');
25
+ } catch {}
26
+ });
27
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const sessions = ['alpha', 'beta', 'gamma'].map(a => ({
33
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ }));
36
+ await route.fulfill({ response: r, json: { ...b, sessions } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
39
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
43
+ await page.waitForTimeout(500);
44
+
45
+ const readTy = (alias) => page.evaluate(a => {
46
+ const el = document.querySelector(`g[data-node="${a}"]`);
47
+ if (!el) return null;
48
+ const m = getComputedStyle(el).transform;
49
+ if (!m || m === 'none') return 0;
50
+ // matrix(a, b, c, d, tx, ty) — extract ty
51
+ const parts = m.match(/matrix\(([^)]+)\)/);
52
+ if (!parts) return null;
53
+ const arr = parts[1].split(',').map(s => parseFloat(s.trim()));
54
+ return arr[5] ?? null;
55
+ }, alias);
56
+
57
+ const restAlpha = await readTy('alpha');
58
+ const restBeta = await readTy('beta');
59
+ await page.locator('g[data-node="alpha"]').hover({ force: true });
60
+ await page.waitForTimeout(300);
61
+ const hoveredAlpha = await readTy('alpha');
62
+ const hoveredBeta = await readTy('beta');
63
+ await page.mouse.move(10, 10);
64
+ await page.waitForTimeout(300);
65
+ const releasedAlpha = await readTy('alpha');
66
+
67
+ await ctx.close();
68
+ return { restAlpha, restBeta, hoveredAlpha, hoveredBeta, releasedAlpha };
69
+ }
70
+
71
+ const normal = await probe(false);
72
+ const reduced = await probe(true);
73
+ await browser.close();
74
+
75
+ const results = {
76
+ normal_restAtZero: normal.restAlpha === 0 && normal.restBeta === 0,
77
+ normal_hoveredLifts: normal.hoveredAlpha === -2,
78
+ normal_siblingStays: normal.hoveredBeta === 0,
79
+ normal_released: normal.releasedAlpha === 0,
80
+ reduced_noLift: reduced.hoveredAlpha === 0,
81
+ };
82
+ const ok = Object.values(results).every(Boolean);
83
+ console.log(`${ok ? '✅' : '❌'} node hover lift:`, JSON.stringify(results),
84
+ `\n normal=`, normal, `\n reduced=`, reduced);
85
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,107 @@
1
+ /* Round 147 verification: node tooltip extends R98's flow summary
2
+ * with the actual sender / receiver alias breakdown. The R97 idiom
3
+ * — anywhere the UI shows "N" should hover-explain WHICH N — was
4
+ * applied to filter pills, group labels, vendor letters, pressure
5
+ * segments, recent-row, active-links chip. The node title was the
6
+ * last surface showing only aggregate counts ("12 in / 5 out");
7
+ * R147 adds peer lines.
8
+ *
9
+ * Fleet:
10
+ * alpha — sends 3 msgs to beta, 5 to gamma, 2 to delta
11
+ * beta — sends 4 msgs to alpha, 1 to gamma
12
+ * gamma — quiet (receiver only)
13
+ * delta — quiet (receiver only)
14
+ *
15
+ * For alpha:
16
+ * flowOut = 3 + 5 + 2 = 10
17
+ * flowIn = 4 (from beta)
18
+ * senders ← beta (4)
19
+ * receivers → beta (3), gamma (5), delta (2) — sorted by count desc:
20
+ * gamma (5), beta (3), delta (2)
21
+ *
22
+ * Title should contain:
23
+ * "flows: 4 in / 10 out"
24
+ * "← from: beta (4)"
25
+ * "→ to: gamma (5), beta (3), delta (2)" (sorted desc by count)
26
+ *
27
+ * Also verify a node with no flows (gamma will have 5 inbound from alpha
28
+ * and 1 from beta; let's pick delta which only has 2 inbound from alpha).
29
+ */
30
+ import { chromium } from 'playwright';
31
+ import { readFileSync } from 'node:fs';
32
+
33
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
34
+ const browser = await chromium.launch({ headless: true });
35
+
36
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
37
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
38
+ await ctx.addInitScript(() => {
39
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
40
+ });
41
+
42
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
43
+ await ctx.route('**/api/hub/status*', async (route) => {
44
+ const r = await route.fetch();
45
+ const b = await r.json();
46
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
47
+ const mk = (alias, status) => ({
48
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
49
+ network_id: nid, project_dir: null,
50
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
51
+ });
52
+ await route.fulfill({ response: r, json: { ...b, sessions: [
53
+ mk('alpha', 'working'), mk('beta', 'working'),
54
+ mk('gamma', 'idle'), mk('delta', 'idle'),
55
+ ] } });
56
+ });
57
+
58
+ const now = Date.now();
59
+ const mkMsg = (id, from, to, ageMs) => ({
60
+ id, from_alias: from, to_alias: to, content: 'hi',
61
+ network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
62
+ });
63
+ const msgs = [];
64
+ let id = 0;
65
+ // alpha → beta (3), gamma (5), delta (2)
66
+ for (let i = 0; i < 3; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'beta', 10000 + id * 100));
67
+ for (let i = 0; i < 5; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'gamma', 10000 + id * 100));
68
+ for (let i = 0; i < 2; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'delta', 10000 + id * 100));
69
+ // beta → alpha (4), gamma (1)
70
+ for (let i = 0; i < 4; i++) msgs.push(mkMsg(`m${id++}`, 'beta', 'alpha', 10000 + id * 100));
71
+ for (let i = 0; i < 1; i++) msgs.push(mkMsg(`m${id++}`, 'beta', 'gamma', 10000 + id * 100));
72
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
73
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
74
+
75
+ const page = await ctx.newPage();
76
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
77
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
78
+ await page.waitForTimeout(400);
79
+
80
+ const readTitle = (alias) => page.evaluate((a) => {
81
+ const node = document.querySelector(`g[data-node="${a}"]`);
82
+ return node?.querySelector('title')?.textContent || '';
83
+ }, alias);
84
+
85
+ const alphaTitle = await readTitle('alpha');
86
+ const deltaTitle = await readTitle('delta');
87
+
88
+ await browser.close();
89
+
90
+ const results = {
91
+ // alpha: flows aggregate
92
+ alpha_flowSummary: alphaTitle.includes('flows: 4 in / 10 out'),
93
+ // alpha: senders line
94
+ alpha_sendersLine: alphaTitle.includes('← from: beta (4)'),
95
+ // alpha: receivers line — must be sorted desc by count (gamma 5, beta 3, delta 2)
96
+ alpha_receiversOrder: alphaTitle.includes('→ to: gamma (5), beta (3), delta (2)'),
97
+
98
+ // delta: receiver only, only inbound from alpha (2 msgs)
99
+ delta_flowSummary: deltaTitle.includes('flows: 2 in / 0 out'),
100
+ delta_sendersLine: deltaTitle.includes('← from: alpha (2)'),
101
+ delta_noReceivers: !deltaTitle.includes('→ to:'),
102
+ };
103
+ const ok = Object.values(results).every(Boolean);
104
+ console.log(`${ok ? '✅' : '❌'} node title peers:`, JSON.stringify(results),
105
+ `\n alpha title:\n${alphaTitle}`,
106
+ `\n delta title:\n${deltaTitle}`);
107
+ process.exit(ok ? 0 : 1);