@sleep2agi/agent-network-dashboard 0.4.6-preview.39 → 0.4.6-preview.396

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 (545) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -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 +16 -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/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
  37. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
  38. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
  41. package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
  42. package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
  43. package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
  44. package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
  45. package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
  46. package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
  47. package/.next/server/app/index.html +2 -2
  48. package/.next/server/app/index.rsc +14 -14
  49. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  50. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/login.html +2 -2
  56. package/.next/server/app/login.rsc +14 -14
  57. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  58. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  59. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  60. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  61. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  62. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  63. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app/logs.html +2 -2
  65. package/.next/server/app/logs.rsc +14 -14
  66. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  67. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  68. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  69. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  70. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  71. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  72. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  73. package/.next/server/app/messages.html +2 -2
  74. package/.next/server/app/messages.rsc +14 -14
  75. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  76. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  77. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  78. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  80. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  81. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/node.html +2 -2
  83. package/.next/server/app/node.rsc +14 -14
  84. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  85. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  86. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  87. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  88. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  89. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  90. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  91. package/.next/server/app/nodes.html +2 -2
  92. package/.next/server/app/nodes.rsc +14 -14
  93. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  94. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  95. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  96. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  97. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  98. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  99. package/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  101. package/.next/server/app/server-logs.html +2 -2
  102. package/.next/server/app/server-logs.rsc +14 -14
  103. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  104. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  105. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  106. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  107. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  108. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  109. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  110. package/.next/server/app/settings/networks.html +2 -2
  111. package/.next/server/app/settings/networks.rsc +14 -14
  112. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  113. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  114. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  115. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  116. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  117. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  118. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  119. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  121. package/.next/server/app/settings/tokens.html +2 -2
  122. package/.next/server/app/settings/tokens.rsc +14 -14
  123. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  124. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  125. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  126. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  127. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  128. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  129. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  130. package/.next/server/app/settings.html +2 -2
  131. package/.next/server/app/settings.rsc +14 -14
  132. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  133. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  134. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  135. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  136. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  137. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  138. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  139. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  140. package/.next/server/app/tasks.html +2 -2
  141. package/.next/server/app/tasks.rsc +14 -14
  142. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  143. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  144. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  145. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  146. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  147. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  148. package/.next/server/app-paths-manifest.json +2 -0
  149. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
  150. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
  151. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
  152. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
  153. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
  154. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
  155. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
  156. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
  157. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
  158. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
  159. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  160. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  161. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  162. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  163. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  164. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  174. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  175. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  176. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  177. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  178. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  179. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  180. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  181. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  182. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  183. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  184. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  185. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  186. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  187. package/.next/server/middleware-build-manifest.js +3 -3
  188. package/.next/server/pages/404.html +2 -2
  189. package/.next/server/pages/500.html +1 -1
  190. package/.next/server/server-reference-manifest.js +1 -1
  191. package/.next/server/server-reference-manifest.json +1 -1
  192. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  193. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  194. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  195. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  196. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  197. package/.next/static/chunks/06nb5y2kl41td.js +1 -0
  198. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  199. package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
  200. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  201. package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
  202. package/.next/static/chunks/0ql1lg_6gwlv8.js +4 -0
  203. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  204. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  205. package/.next/static/chunks/117g0j1~f0vr..js +1 -0
  206. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  207. package/.next/trace +2 -2
  208. package/.next/trace-build +1 -1
  209. package/.next/types/routes.d.ts +3 -1
  210. package/.next/types/validator.ts +18 -0
  211. package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
  212. package/app/api/hub/server/[hostname]/health/route.ts +58 -0
  213. package/app/api/hub/servers/route.ts +88 -1
  214. package/app/components/ChatPopover.tsx +38 -2
  215. package/app/components/CommandPalette.tsx +107 -0
  216. package/app/components/HelpOverlay.tsx +109 -1
  217. package/app/components/ServersDrawer.tsx +181 -5
  218. package/app/components/TopoGraph.tsx +6492 -270
  219. package/app/components/utils.ts +8 -7
  220. package/app/globals.css +110 -5
  221. package/app/lib/time.ts +36 -0
  222. package/app/lib/vendorIdentity.ts +14 -3
  223. package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
  224. package/package.json +2 -2
  225. package/public/sleep2agi-logo.svg +11 -0
  226. package/public/vendors/claude.svg +11 -0
  227. package/public/vendors/minimax.svg +11 -0
  228. package/public/vendors/openai.svg +12 -0
  229. package/screenshots/r293-p0/after-full.png +0 -0
  230. package/screenshots/r293-p0/after-topo.png +0 -0
  231. package/screenshots/r293-p0/baseline-full.png +0 -0
  232. package/screenshots/r293-p0/baseline-topo.png +0 -0
  233. package/screenshots/r293-p0/logo-integrated-full.png +0 -0
  234. package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
  235. package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
  236. package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
  237. package/screenshots/r293-p0/wave1-after-full.png +0 -0
  238. package/screenshots/r293-p0/wave1-after-topo.png +0 -0
  239. package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
  240. package/scripts/p0-topo-screenshot.mjs +82 -0
  241. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  242. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  243. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  244. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  245. package/scripts/topo-active-links-chip-test.mjs +73 -0
  246. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  247. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  248. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  249. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  250. package/scripts/topo-brand-logo-transition-test.mjs +94 -0
  251. package/scripts/topo-brand-watermark-test.mjs +97 -0
  252. package/scripts/topo-canvas-aria-test.mjs +108 -0
  253. package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
  254. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  255. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  256. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  257. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  258. package/scripts/topo-chip-focus-test.mjs +95 -0
  259. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  260. package/scripts/topo-chip-row-gap-test.mjs +90 -0
  261. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  262. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  263. package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
  264. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  265. package/scripts/topo-chrome-active-weight-test.mjs +104 -0
  266. package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
  267. package/scripts/topo-chrome-gap-test.mjs +73 -0
  268. package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
  269. package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
  270. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  271. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  272. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  273. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  274. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  275. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  276. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  277. package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
  278. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  279. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  280. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  281. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  282. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  283. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  284. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  285. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  286. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  287. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  288. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  289. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  290. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  291. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  292. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  293. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  294. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  295. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  296. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  297. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  298. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  299. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  300. package/scripts/topo-edge-fadein-test.mjs +106 -0
  301. package/scripts/topo-edge-focus-test.mjs +113 -0
  302. package/scripts/topo-edge-hover-test.mjs +93 -0
  303. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  304. package/scripts/topo-edge-pill-test.mjs +105 -0
  305. package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
  306. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  307. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  308. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  309. package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
  310. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  311. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  312. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  313. package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
  314. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  315. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  316. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  317. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  318. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  319. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  320. package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
  321. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  322. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  323. package/scripts/topo-freshness-chip-test.mjs +74 -0
  324. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  325. package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
  326. package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
  327. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  328. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  329. package/scripts/topo-group-count-test.mjs +6 -1
  330. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  331. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  332. package/scripts/topo-group-label-hover-test.mjs +119 -0
  333. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  334. package/scripts/topo-group-label-tint-test.mjs +99 -0
  335. package/scripts/topo-group-label-title-test.mjs +83 -0
  336. package/scripts/topo-group-pin-test.mjs +119 -0
  337. package/scripts/topo-group-pip-fadein-test.mjs +104 -0
  338. package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
  339. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  340. package/scripts/topo-group-status-pips-test.mjs +103 -0
  341. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  342. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  343. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  344. package/scripts/topo-groupbox-live-test.mjs +100 -0
  345. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  346. package/scripts/topo-groupbox-test.mjs +4 -2
  347. package/scripts/topo-header-gap-test.mjs +94 -0
  348. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  349. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  350. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  351. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  352. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  353. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  354. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  355. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  356. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  357. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  358. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  359. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  360. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  361. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  362. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  363. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  364. package/scripts/topo-hub-a11y-test.mjs +112 -0
  365. package/scripts/topo-hub-busyness-test.mjs +100 -0
  366. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  367. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  368. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  369. package/scripts/topo-hub-fadein-test.mjs +88 -0
  370. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  371. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  372. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  373. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  374. package/scripts/topo-hub-title-test.mjs +93 -0
  375. package/scripts/topo-hub-working-count-test.mjs +99 -0
  376. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  377. package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
  378. package/scripts/topo-kicker-color-test.mjs +93 -0
  379. package/scripts/topo-kicker-tracking-test.mjs +101 -0
  380. package/scripts/topo-kicker-weight-test.mjs +98 -0
  381. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  382. package/scripts/topo-label-lift-test.mjs +5 -1
  383. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  384. package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
  385. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  386. package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
  387. package/scripts/topo-layout-toggle-test.mjs +131 -0
  388. package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
  389. package/scripts/topo-legend-compress-test.mjs +107 -0
  390. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  391. package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
  392. package/scripts/topo-legend-count-tabular-test.mjs +87 -0
  393. package/scripts/topo-legend-count-test.mjs +88 -0
  394. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  395. package/scripts/topo-legend-count-weight-test.mjs +86 -0
  396. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  397. package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
  398. package/scripts/topo-legend-hover-test.mjs +113 -0
  399. package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
  400. package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
  401. package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
  402. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  403. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  404. package/scripts/topo-legend-pin-test.mjs +128 -0
  405. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  406. package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
  407. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  408. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  409. package/scripts/topo-legend-row-title-test.mjs +102 -0
  410. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  411. package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
  412. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  413. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  414. package/scripts/topo-minimap-radius-test.mjs +88 -0
  415. package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
  416. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  417. package/scripts/topo-mobile-chips-test.mjs +103 -0
  418. package/scripts/topo-monogram-font-test.mjs +112 -0
  419. package/scripts/topo-monogram-stroke-test.mjs +101 -0
  420. package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
  421. package/scripts/topo-node-a11y-test.mjs +107 -0
  422. package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
  423. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  424. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  425. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  426. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  427. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  428. package/scripts/topo-node-title-peers-test.mjs +107 -0
  429. package/scripts/topo-node-title-test.mjs +101 -0
  430. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  431. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  432. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  433. package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
  434. package/scripts/topo-panel-elevation-test.mjs +76 -0
  435. package/scripts/topo-panel-fadein-test.mjs +102 -0
  436. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  437. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  438. package/scripts/topo-panel-rx-test.mjs +90 -0
  439. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  440. package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
  441. package/scripts/topo-panel-title-tracking-test.mjs +78 -0
  442. package/scripts/topo-particle-stagger-test.mjs +82 -0
  443. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  444. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  445. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  446. package/scripts/topo-pin-intersection-test.mjs +121 -0
  447. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  448. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  449. package/scripts/topo-pin-persistence-test.mjs +105 -0
  450. package/scripts/topo-popover-meta-test.mjs +78 -0
  451. package/scripts/topo-prefix-opacity-test.mjs +107 -0
  452. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  453. package/scripts/topo-pressure-hover-test.mjs +123 -0
  454. package/scripts/topo-pressure-pin-test.mjs +119 -0
  455. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  456. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  457. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  458. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  459. package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
  460. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  461. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  462. package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
  463. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  464. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  465. package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
  466. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  467. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  468. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  469. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  470. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  471. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  472. package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
  473. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  474. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  475. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  476. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  477. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  478. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  479. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  480. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  481. package/scripts/topo-recent-row-title-test.mjs +112 -0
  482. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  483. package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
  484. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  485. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  486. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  487. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  488. package/scripts/topo-reduced-motion-test.mjs +77 -0
  489. package/scripts/topo-reset-spin-test.mjs +120 -0
  490. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  491. package/scripts/topo-spokes-retired-test.mjs +93 -0
  492. package/scripts/topo-starfield-density-test.mjs +89 -0
  493. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  494. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  495. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  496. package/scripts/topo-svg-focus-test.mjs +112 -0
  497. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  498. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  499. package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
  500. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  501. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  502. package/scripts/topo-tier-ring-test.mjs +69 -0
  503. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  504. package/scripts/topo-tier-stagger-test.mjs +89 -0
  505. package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
  506. package/scripts/topo-title-tracking-test.mjs +98 -0
  507. package/scripts/topo-titleblock-gap-test.mjs +86 -0
  508. package/scripts/topo-titleblock-margin-test.mjs +83 -0
  509. package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
  510. package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
  511. package/scripts/topo-tz-ghost-test.mjs +69 -0
  512. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  513. package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
  514. package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
  515. package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
  516. package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
  517. package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
  518. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  519. package/scripts/topo-vendor-pill-test.mjs +125 -0
  520. package/scripts/topo-vendor-pin-test.mjs +125 -0
  521. package/scripts/topo-view-smooth-test.mjs +131 -0
  522. package/scripts/topo-watermark-tracking-test.mjs +99 -0
  523. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  524. package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
  525. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  526. package/scripts/topo-wrapper-radius-test.mjs +73 -0
  527. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  528. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  529. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  530. package/.next/static/chunks/06tr6yzu4mb8_.js +0 -1
  531. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  532. package/.next/static/chunks/0c_yjgkf2~vd4.js +0 -1
  533. package/.next/static/chunks/0dev7f7k0p604.js +0 -1
  534. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  535. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  536. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  537. package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
  538. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  539. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  540. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  541. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  542. package/.next/static/chunks/13.zti9ix.1eh.css +0 -2
  543. /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_buildManifest.js +0 -0
  544. /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_clientMiddlewareManifest.js +0 -0
  545. /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_ssgManifest.js +0 -0
@@ -0,0 +1,63 @@
1
+ /* Round 70 verification: HelpOverlay topology section surfaces the R69
2
+ * Cmd+K palette pin actions. The 1 new entry sits alongside R65's R63
3
+ * + R64 entries; this test re-verifies the full chain so a future edit
4
+ * accidentally dropping any rung gets caught here.
5
+ */
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
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
13
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
14
+ await ctx.addInitScript(() => {
15
+ try {
16
+ localStorage.setItem('anet-theme', 'cyber');
17
+ sessionStorage.setItem('anet_v3_auth', '1');
18
+ } catch {}
19
+ });
20
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
21
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
22
+ const page = await ctx.newPage();
23
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
24
+ await page.waitForTimeout(800);
25
+
26
+ await page.keyboard.press('?');
27
+ await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
28
+
29
+ const items = await page.evaluate(() => {
30
+ const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
31
+ if (!dialog) return [];
32
+ const groups = [...dialog.querySelectorAll('div')];
33
+ let active = null;
34
+ for (const g of groups) {
35
+ if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
36
+ active = g.parentElement;
37
+ break;
38
+ }
39
+ }
40
+ return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
41
+ });
42
+
43
+ await browser.close();
44
+ if (items.length === 0) { console.log('❌ topology section not found'); process.exit(1); }
45
+
46
+ const has = (s) => items.some(t => t.includes(s));
47
+ const results = {
48
+ r70_cmdkPin: has('Pin / clear filters via palette') && has('(R69)'),
49
+ // R65 priors
50
+ r63_groupPin: has('Pin a team focus (R63)'),
51
+ r64_clearOne: has('Clear one filter (R64)'),
52
+ // R62 priors
53
+ r60_pinChip: has('Pin a status filter (R60/R61)'),
54
+ r62_esc: has('Clear pinned filter (R62)'),
55
+ // R59 priors
56
+ r52_clickHub: has('Fit topology to canvas (R52)'),
57
+ r55_legendHover: has('Highlight nodes of a status (R55)'),
58
+ r56_signalHover: has('Highlight one flow edge (R56)'),
59
+ };
60
+ const ok = Object.values(results).every(Boolean);
61
+ console.log(`${ok ? '✅' : '❌'} help overlay R70:`, JSON.stringify(results),
62
+ `\n items=`, items);
63
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,59 @@
1
+ /* Round 78 verification: HelpOverlay surfaces the R73 (click pill body
2
+ * to clear) + R77 (hover N active links to brighten all flows) gestures.
3
+ * Both shipped as real user gestures but weren't in the discoverability
4
+ * surface.
5
+ */
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
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
13
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
14
+ await ctx.addInitScript(() => {
15
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
16
+ });
17
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
18
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
19
+ const page = await ctx.newPage();
20
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
21
+ await page.waitForTimeout(800);
22
+
23
+ await page.keyboard.press('?');
24
+ await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
25
+
26
+ const items = await page.evaluate(() => {
27
+ const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
28
+ const groups = [...(dialog?.querySelectorAll('div') || [])];
29
+ let active = null;
30
+ for (const g of groups) {
31
+ if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
32
+ active = g.parentElement;
33
+ break;
34
+ }
35
+ }
36
+ return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
37
+ });
38
+
39
+ await browser.close();
40
+
41
+ const has = (s) => items.some(t => t.includes(s));
42
+ const results = {
43
+ r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
44
+ r78_chipHover: has('Brighten all active flows — hover'),
45
+ // R74 and R70 priors should still appear (no regression)
46
+ r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
47
+ r70_cmdkPin: has('(R69)'),
48
+ // R65 priors
49
+ r63_groupPin: has('Pin a team focus (R63)'),
50
+ r64_clearOne: has('Clear one filter (R64)'),
51
+ // R59 priors
52
+ r52_clickHub: has('Fit topology to canvas (R52)'),
53
+ r55_legendHover: has('Highlight nodes of a status (R55)'),
54
+ r56_signalHover: has('Highlight one flow edge (R56)'),
55
+ };
56
+ const ok = Object.values(results).every(Boolean);
57
+ console.log(`${ok ? '✅' : '❌'} help overlay R78:`, JSON.stringify(results),
58
+ `\n items=`, items);
59
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,58 @@
1
+ /* Round 81 verification: HelpOverlay surfaces R79 (status chip hover) +
2
+ * R80 (vendor chip letter hover). Both gestures landed without entries
3
+ * in the discoverability surface. Continues the R59/R62/R65/R70/R78
4
+ * docs-catch-up cadence.
5
+ */
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
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
13
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
14
+ await ctx.addInitScript(() => {
15
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
16
+ });
17
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
18
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
19
+ const page = await ctx.newPage();
20
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
21
+ await page.waitForTimeout(800);
22
+
23
+ await page.keyboard.press('?');
24
+ await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
25
+
26
+ const items = await page.evaluate(() => {
27
+ const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
28
+ const groups = [...(dialog?.querySelectorAll('div') || [])];
29
+ let active = null;
30
+ for (const g of groups) {
31
+ if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
32
+ active = g.parentElement;
33
+ break;
34
+ }
35
+ }
36
+ return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
37
+ });
38
+
39
+ await browser.close();
40
+
41
+ const has = (s) => items.some(t => t.includes(s));
42
+ const results = {
43
+ r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
44
+ r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
45
+ // R78 priors
46
+ r73_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
47
+ r77_activeLinks: has('Brighten all active flows — hover'),
48
+ // R74 / R70 / R65 / R59 priors
49
+ r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
50
+ r70_cmdkPin: has('(R69)'),
51
+ r63_groupPin: has('Pin a team focus (R63)'),
52
+ r60_pinChip: has('Pin a status filter (R60/R61)'),
53
+ r52_clickHub: has('Fit topology to canvas (R52)'),
54
+ };
55
+ const ok = Object.values(results).every(Boolean);
56
+ console.log(`${ok ? '✅' : '❌'} help overlay R81:`, JSON.stringify(results),
57
+ `\n items=`, items);
58
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,64 @@
1
+ /* Round 87 verification: HelpOverlay catches up on R83 (pressure-bar
2
+ * segment hover preview) + R86 (group-label hover preview). Continues
3
+ * the R59/R62/R65/R70/R78/R81 docs-catch-up cadence (~5 rounds).
4
+ *
5
+ * Stacks five generations of help-overlay assertions — any future
6
+ * regression that drops an entry will be caught by one of:
7
+ * R87 (this) → R81 → R78 → R74 → R70 → R65 → R59
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const browser = await chromium.launch({ headless: true });
14
+
15
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
19
+ });
20
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
21
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
22
+ const page = await ctx.newPage();
23
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
24
+ await page.waitForTimeout(800);
25
+
26
+ await page.keyboard.press('?');
27
+ await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
28
+
29
+ const items = await page.evaluate(() => {
30
+ const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
31
+ const groups = [...(dialog?.querySelectorAll('div') || [])];
32
+ let active = null;
33
+ for (const g of groups) {
34
+ if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
35
+ active = g.parentElement;
36
+ break;
37
+ }
38
+ }
39
+ return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
40
+ });
41
+
42
+ await browser.close();
43
+
44
+ const has = (s) => items.some(t => t.includes(s));
45
+ const results = {
46
+ r87_segHover: has('Preview a status filter — hover a pressure-bar segment (R83)'),
47
+ r87_groupLabelHover: has('Preview a team focus — hover a group label (R86)'),
48
+ // R81 priors
49
+ r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
50
+ r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
51
+ // R78 priors
52
+ r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
53
+ r78_activeLinks: has('Brighten all active flows — hover'),
54
+ // R74 / R70 / R65 / R59 priors — five generations of docs catch-up
55
+ r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
56
+ r70_cmdkPin: has('(R69)'),
57
+ r65_groupClick: has('Pin a team focus (R63)'),
58
+ r62_pinChip: has('Pin a status filter (R60/R61)'),
59
+ r59_clickHub: has('Fit topology to canvas (R52)'),
60
+ };
61
+ const ok = Object.values(results).every(Boolean);
62
+ console.log(`${ok ? '✅' : '❌'} help overlay R87:`, JSON.stringify(results),
63
+ `\n items=`, items);
64
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,65 @@
1
+ /* Round 91 verification: HelpOverlay catches up on R88 vendor click-
2
+ * to-pin and R90 granular Cmd+K vendor-clear. Continues the
3
+ * R59/R62/R65/R70/R78/R81/R87 docs-catch-up cadence.
4
+ *
5
+ * Seven generations of help-overlay assertions stacked here — any
6
+ * future regression that drops an entry will be caught by one of
7
+ * R91 → R87 → R81 → R78 → R74 → R70 → R65 → R59.
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const browser = await chromium.launch({ headless: true });
14
+
15
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
19
+ });
20
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
21
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
22
+ const page = await ctx.newPage();
23
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
24
+ await page.waitForTimeout(800);
25
+
26
+ await page.keyboard.press('?');
27
+ await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
28
+
29
+ const items = await page.evaluate(() => {
30
+ const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
31
+ const groups = [...(dialog?.querySelectorAll('div') || [])];
32
+ let active = null;
33
+ for (const g of groups) {
34
+ if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
35
+ active = g.parentElement;
36
+ break;
37
+ }
38
+ }
39
+ return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
40
+ });
41
+
42
+ await browser.close();
43
+
44
+ const has = (s) => items.some(t => t.includes(s));
45
+ const results = {
46
+ r91_vendorPin: has('Pin a vendor filter — click A/O/书/? in the chip row (R88)'),
47
+ r91_cmdkVendorClear: has('Clear vendor pin only via palette → "Clear topology vendor filter" (R90)'),
48
+ // R87 priors (hover preview pair)
49
+ r87_segHover: has('Preview a status filter — hover a pressure-bar segment (R83)'),
50
+ r87_groupLabelHover: has('Preview a team focus — hover a group label (R86)'),
51
+ // R81 priors (chip-row hover pair)
52
+ r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
53
+ r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
54
+ // R78 / R74 / R70 / R65 / R59 priors — seven generations
55
+ r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
56
+ r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
57
+ r70_cmdkPin: has('(R69)'),
58
+ r65_groupClick: has('Pin a team focus (R63)'),
59
+ r62_pinChip: has('Pin a status filter (R60/R61)'),
60
+ r59_clickHub: has('Fit topology to canvas (R52)'),
61
+ };
62
+ const ok = Object.values(results).every(Boolean);
63
+ console.log(`${ok ? '✅' : '❌'} help overlay R91:`, JSON.stringify(results),
64
+ `\n items=`, items);
65
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,117 @@
1
+ /* Round 223 verification: recent-signal panel header hot-tail tspan
2
+ * ("· N hot") always-mounts; visibility crossfades via opacity gate
3
+ * instead of conditional mount/unmount on hotFlowCount > 0. R190's
4
+ * anet-fade-in entrance is preserved; R223 makes the EXIT also ease.
5
+ *
6
+ * Test scenarios:
7
+ * A. 0 hot flows (all counts < 10):
8
+ * · tspan IN DOM (always-mount proven)
9
+ * · data-recent-panel-hot-visible='false'
10
+ * · opacity attribute = 0
11
+ * · text content empty (no "· 0 hot" artifact)
12
+ * B. 1 hot flow (count=12):
13
+ * · visible='true', opacity=1
14
+ * · text content matches "· 1 hot"
15
+ * · anet-fade-in className retained for R190 compat
16
+ * Both: inline style.transition includes 'opacity 300ms'.
17
+ */
18
+ import { chromium } from 'playwright';
19
+ import { readFileSync } from 'node:fs';
20
+
21
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
22
+ const browser = await chromium.launch({ headless: true });
23
+
24
+ async function setup(messages) {
25
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
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) => ({
36
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages } }));
45
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.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.waitForSelector('[data-recent-panel-hot-count]', { timeout: 10000, state: 'attached' });
50
+ await page.waitForTimeout(400);
51
+ return page;
52
+ }
53
+
54
+ const probe = async (page) => page.evaluate(() => {
55
+ const tail = document.querySelector('[data-recent-panel-hot-count]');
56
+ if (!tail) return null;
57
+ return {
58
+ present: true,
59
+ visible: tail.getAttribute('data-recent-panel-hot-visible'),
60
+ count: tail.getAttribute('data-recent-panel-hot-count'),
61
+ opacity: parseFloat(tail.getAttribute('opacity') || ''),
62
+ text: tail.textContent,
63
+ className: tail.getAttribute('class') || '',
64
+ transition: tail.style.transition,
65
+ };
66
+ });
67
+
68
+ const now = Date.now();
69
+
70
+ // Scenario A: 0 hot flows — 1 cold flow with 3 messages (still cold, < 10)
71
+ const coldMsgs = [];
72
+ for (let i = 0; i < 3; i++) {
73
+ coldMsgs.push({
74
+ id: `cold${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
75
+ network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString(),
76
+ });
77
+ }
78
+ const pageA = await setup(coldMsgs);
79
+ const probeA = await probe(pageA);
80
+ await pageA.close();
81
+
82
+ // Scenario B: 1 hot flow — 12 messages from alpha to beta
83
+ const hotMsgs = [];
84
+ for (let i = 0; i < 12; i++) {
85
+ hotMsgs.push({
86
+ id: `hot${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
87
+ network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString(),
88
+ });
89
+ }
90
+ const pageB = await setup(hotMsgs);
91
+ const probeB = await probe(pageB);
92
+ await pageB.close();
93
+ await browser.close();
94
+
95
+ const results = {
96
+ // A: 0 hot flows — tspan in DOM but hidden
97
+ A_present_in_dom: probeA?.present === true,
98
+ A_visible_false: probeA?.visible === 'false',
99
+ A_opacity_0: probeA?.opacity === 0,
100
+ A_text_empty: probeA?.text === '' || probeA?.text === null,
101
+ A_count_attr_0: probeA?.count === '0',
102
+ A_has_transition: /opacity\s+(?:300ms|0\.3s)/.test(probeA?.transition || ''),
103
+
104
+ // B: 1 hot flow — visible
105
+ B_present_in_dom: probeB?.present === true,
106
+ B_visible_true: probeB?.visible === 'true',
107
+ B_opacity_1: probeB?.opacity === 1,
108
+ B_text_one_hot: /·\s*1\s*hot/.test(probeB?.text || ''),
109
+ B_count_attr_1: probeB?.count === '1',
110
+ B_fade_class_present: (probeB?.className || '').split(/\s+/).includes('anet-fade-in'),
111
+ B_has_transition: /opacity\s+(?:300ms|0\.3s)/.test(probeB?.transition || ''),
112
+ };
113
+ const ok = Object.values(results).every(Boolean);
114
+ console.log(`${ok ? '✅' : '❌'} hot-tail crossfade:`, JSON.stringify(results),
115
+ `\n probeA (0 hot):`, probeA,
116
+ `\n probeB (1 hot):`, probeB);
117
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,120 @@
1
+ /* Round 261 verification: HTML overlay padding aligned with SVG corner
2
+ * panel padding — chrome strip + minimap both at 16 CSS px from canvas
3
+ * edges, matching the SVG corner panels at (16, 16) panel-translate.
4
+ *
5
+ * Pre-R261:
6
+ * SVG panels: 16 SVG units from edges (≈ 15 CSS px @ ~0.94× scale)
7
+ * chrome: 12 CSS px (bottom-3 right-3) — ~3px optical asymmetry
8
+ * minimap: 12 CSS px (right-3) — same asymmetry
9
+ *
10
+ * Post-R261:
11
+ * chrome: bottom-4 right-4 → 16 CSS px from container edges
12
+ * minimap: right-4 → 16 CSS px from container right edge
13
+ *
14
+ * 16 CSS px ≈ 17 SVG units, slightly tighter than the SVG 16-unit
15
+ * inset due to render-scale rounding, but visually matches the SVG
16
+ * panel padding so HTML overlays and SVG panels speak one inset
17
+ * vocabulary.
18
+ *
19
+ * Test scope:
20
+ * 1. Chrome strip's right edge sits at ~16 CSS px from container
21
+ * right edge (allow ±2 px sub-pixel slop).
22
+ * 2. Chrome strip's bottom edge sits at ~16 CSS px from container
23
+ * bottom edge (±2 slop).
24
+ * 3. Minimap (mounted after zoom-in × 3): right edge at ~16 CSS px.
25
+ * 4. R255 chrome-strip semantic gap intact: fleet→view gap still ≥ 11.
26
+ */
27
+ import { chromium } from 'playwright';
28
+ import { readFileSync } from 'node:fs';
29
+
30
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
31
+ const browser = await chromium.launch({ headless: true });
32
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
33
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
34
+ await ctx.addInitScript(() => {
35
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
36
+ });
37
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
38
+ await ctx.route('**/api/hub/status*', async (route) => {
39
+ const r = await route.fetch();
40
+ const b = await r.json();
41
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
42
+ const mk = (alias) => ({
43
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
44
+ network_id: nid, project_dir: null,
45
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
46
+ });
47
+ await route.fulfill({ response: r, json: { ...b, sessions: [
48
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
49
+ ] } });
50
+ });
51
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
52
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
53
+
54
+ const page = await ctx.newPage();
55
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
56
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
57
+ await page.waitForSelector('[data-topo-chrome]', { timeout: 10000 });
58
+ await page.waitForSelector('[data-topo-wrapper]', { timeout: 10000 });
59
+ await page.waitForTimeout(300);
60
+
61
+ const beforeMinimap = await page.evaluate(() => {
62
+ const wrapper = document.querySelector('[data-topo-wrapper]');
63
+ const chrome = document.querySelector('[data-topo-chrome]');
64
+ const fleet = document.querySelector('[data-topo-chrome-fleet-group-trailer]');
65
+ const view = document.querySelector('[data-topo-chrome-view-group-leader]');
66
+ const wrR = wrapper?.getBoundingClientRect() ?? null;
67
+ const chR = chrome?.getBoundingClientRect() ?? null;
68
+ return {
69
+ wrapperRight: wrR ? wrR.right : null,
70
+ wrapperBottom: wrR ? wrR.bottom : null,
71
+ chromeRight: chR ? chR.right : null,
72
+ chromeBottom: chR ? chR.bottom : null,
73
+ fleetRight: fleet ? fleet.getBoundingClientRect().right : null,
74
+ viewLeft: view ? view.getBoundingClientRect().left : null,
75
+ };
76
+ });
77
+
78
+ // Zoom in to force minimap mount
79
+ for (let i = 0; i < 3; i++) {
80
+ await page.locator('[data-topo-chrome-zoom-in]').click();
81
+ await page.waitForTimeout(120);
82
+ }
83
+ await page.waitForSelector('[data-topo-minimap]', { timeout: 5000 });
84
+ await page.waitForTimeout(300);
85
+
86
+ const minimapProbe = await page.evaluate(() => {
87
+ const wrapper = document.querySelector('[data-topo-wrapper]');
88
+ const mm = document.querySelector('[data-topo-minimap]');
89
+ const wrR = wrapper?.getBoundingClientRect() ?? null;
90
+ const mmR = mm?.getBoundingClientRect() ?? null;
91
+ return {
92
+ wrapperRight: wrR ? wrR.right : null,
93
+ minimapRight: mmR ? mmR.right : null,
94
+ };
95
+ });
96
+ await browser.close();
97
+
98
+ const chromeRightOffset = (beforeMinimap.wrapperRight != null && beforeMinimap.chromeRight != null)
99
+ ? (beforeMinimap.wrapperRight - beforeMinimap.chromeRight) : null;
100
+ const chromeBottomOffset = (beforeMinimap.wrapperBottom != null && beforeMinimap.chromeBottom != null)
101
+ ? (beforeMinimap.wrapperBottom - beforeMinimap.chromeBottom) : null;
102
+ const minimapRightOffset = (minimapProbe.wrapperRight != null && minimapProbe.minimapRight != null)
103
+ ? (minimapProbe.wrapperRight - minimapProbe.minimapRight) : null;
104
+ const fleetToViewGap = (beforeMinimap.fleetRight != null && beforeMinimap.viewLeft != null)
105
+ ? (beforeMinimap.viewLeft - beforeMinimap.fleetRight) : null;
106
+
107
+ const inWindow = (v, target, slop) => v != null && Math.abs(v - target) <= slop;
108
+
109
+ const results = {
110
+ chrome_right_at_16: inWindow(chromeRightOffset, 16, 2),
111
+ chrome_bottom_at_16: inWindow(chromeBottomOffset, 16, 2),
112
+ minimap_right_at_16: inWindow(minimapRightOffset, 16, 2),
113
+ r255_fleet_view_gap_intact: fleetToViewGap != null && fleetToViewGap >= 11,
114
+ };
115
+ const ok = Object.values(results).every(Boolean);
116
+ console.log(`${ok ? '✅' : '❌'} HTML overlay padding unify:`, JSON.stringify(results),
117
+ '\n chrome right offset:', chromeRightOffset, '/ bottom:', chromeBottomOffset,
118
+ '\n minimap right offset:', minimapRightOffset,
119
+ '\n R255 fleet→view gap:', fleetToViewGap);
120
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,112 @@
1
+ /* Round 159 verification: hub <g> gains keyboard a11y.
2
+ *
3
+ * R151-R157 a11y sweep covered nodes, group labels, edge badges,
4
+ * recent rows, legend rows, chrome buttons, minimap, chip-row.
5
+ * R158 named the canvas root. The hub <g> — the most visually
6
+ * prominent interactive element on the canvas (R39 enlarged,
7
+ * R43 tooltip, R52 click-to-fitView, R115 hover ring) — was the
8
+ * only major interactive R151-R157 skipped.
9
+ *
10
+ * R159 closes that gap with the same standard pattern:
11
+ * role="button"
12
+ * tabIndex={0}
13
+ * aria-label="Network hub · N online · K working · M links — Enter to fit view"
14
+ * className="anet-topo-svg-focus" (R156 cyan focus ring)
15
+ * onKeyDown(Enter/Space) → fitView() + ripple
16
+ *
17
+ * Test:
18
+ * 1. Mock 3 working + 2 idle sessions + zoom in (so fit-to-view
19
+ * has a visible effect — zoom level moves away from 100%)
20
+ * 2. Assert role / tabIndex / aria-label / className on hub
21
+ * 3. Focus hub + Enter → zoom resets to 100%
22
+ * 4. Confirm aria-label mentions counts + Enter
23
+ */
24
+ import { chromium } from 'playwright';
25
+ import { readFileSync } from 'node:fs';
26
+
27
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
28
+ const browser = await chromium.launch({ headless: true });
29
+
30
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try {
34
+ localStorage.setItem('anet-theme', 'cyber');
35
+ sessionStorage.setItem('anet_v3_auth', '1');
36
+ // R159 hub is gated to layout === 'ring' (line 2946 — grid uses
37
+ // group boxes, no central hub). Force ring.
38
+ localStorage.setItem('anet-topo-layout', 'ring');
39
+ } 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('agents-a1', 'working'), mk('agents-a2', 'working'), mk('agents-a3', 'working'),
54
+ mk('infra-b1', 'idle'), mk('infra-b2', 'idle'),
55
+ ] } });
56
+ });
57
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
58
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
59
+
60
+ const page = await ctx.newPage();
61
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
62
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
63
+ await page.waitForSelector('[data-topo-hub]', { timeout: 10000 });
64
+ await page.waitForTimeout(400);
65
+
66
+ // Probe ARIA before any interaction
67
+ const probe = await page.evaluate(() => {
68
+ const el = document.querySelector('[data-topo-hub]');
69
+ if (!el) return null;
70
+ return {
71
+ tagName: el.tagName.toLowerCase(),
72
+ role: el.getAttribute('role'),
73
+ tabIndex: el.getAttribute('tabindex'),
74
+ ariaLabel: el.getAttribute('aria-label'),
75
+ className: el.getAttribute('class') || '',
76
+ };
77
+ });
78
+
79
+ // Zoom in twice so fitView has a visible effect
80
+ await page.keyboard.press('+');
81
+ await page.keyboard.press('+');
82
+ await page.waitForTimeout(250);
83
+ const zoomBefore = await page.evaluate(() =>
84
+ document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
85
+
86
+ // Focus hub + Enter → fitView resets zoom to 100%
87
+ await page.locator('[data-topo-hub]').focus();
88
+ await page.keyboard.press('Enter');
89
+ await page.waitForTimeout(350);
90
+ const zoomAfter = await page.evaluate(() =>
91
+ document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
92
+
93
+ await browser.close();
94
+
95
+ const label = probe?.ariaLabel || '';
96
+ const results = {
97
+ hub_found: probe !== null,
98
+ is_g: probe?.tagName === 'g',
99
+ role_button: probe?.role === 'button',
100
+ tabIndex_0: probe?.tabIndex === '0',
101
+ label_mentions_online: /5 online/.test(label),
102
+ label_mentions_working: /3 working/.test(label),
103
+ label_mentions_enter: /Enter to fit view/.test(label),
104
+ has_focus_class: (probe?.className || '').includes('anet-topo-svg-focus'),
105
+ zoomedBefore: zoomBefore !== '100%',
106
+ enter_resetsZoom: zoomAfter === '100%',
107
+ };
108
+ const ok = Object.values(results).every(Boolean);
109
+ console.log(`${ok ? '✅' : '❌'} hub a11y:`, JSON.stringify(results),
110
+ `\n probe=`, probe,
111
+ `\n zoomBefore=${zoomBefore} zoomAfter=${zoomAfter}`);
112
+ process.exit(ok ? 0 : 1);