@sleep2agi/agent-network-dashboard 0.4.6-preview.27 → 0.4.6-preview.271

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 (469) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +41 -41
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +6 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +14 -14
  27. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/servers/route.js +7 -0
  37. package/.next/server/app/api/hub/servers/route.js.map +5 -0
  38. package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +14 -14
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +2 -2
  49. package/.next/server/app/login.rsc +14 -14
  50. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +14 -14
  59. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/messages.html +2 -2
  67. package/.next/server/app/messages.rsc +14 -14
  68. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  74. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/node.html +2 -2
  76. package/.next/server/app/node.rsc +14 -14
  77. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  83. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/nodes.html +2 -2
  85. package/.next/server/app/nodes.rsc +14 -14
  86. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  92. package/.next/server/app/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/server-logs.html +2 -2
  95. package/.next/server/app/server-logs.rsc +14 -14
  96. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  97. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  99. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/networks.html +2 -2
  104. package/.next/server/app/settings/networks.rsc +14 -14
  105. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  106. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  112. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  114. package/.next/server/app/settings/tokens.html +2 -2
  115. package/.next/server/app/settings/tokens.rsc +14 -14
  116. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  117. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  119. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  122. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/settings.html +2 -2
  124. package/.next/server/app/settings.rsc +14 -14
  125. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  131. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  132. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/tasks.html +2 -2
  134. package/.next/server/app/tasks.rsc +14 -14
  135. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  136. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  137. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  138. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  140. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  141. package/.next/server/app-paths-manifest.json +1 -0
  142. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
  143. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
  144. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
  145. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
  146. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  147. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  148. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  149. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  150. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  151. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  160. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  174. package/.next/server/middleware-build-manifest.js +3 -3
  175. package/.next/server/pages/404.html +2 -2
  176. package/.next/server/pages/500.html +1 -1
  177. package/.next/server/server-reference-manifest.js +1 -1
  178. package/.next/server/server-reference-manifest.json +1 -1
  179. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  180. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  181. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  182. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  183. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  184. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  185. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  186. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  187. package/.next/static/chunks/0iqr11y9s-xgx.js +1 -0
  188. package/.next/static/chunks/0krph-mr~j~-i.js +1 -0
  189. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  190. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  191. package/.next/static/chunks/0y.7kwty.hl7h.css +2 -0
  192. package/.next/static/chunks/10irw4noos.9c.js +4 -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 +5440 -242
  204. package/app/components/utils.ts +8 -7
  205. package/app/globals.css +110 -5
  206. package/app/lib/time.ts +36 -0
  207. package/package.json +2 -2
  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-self-hover-test.mjs +171 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  216. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  217. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  218. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  219. package/scripts/topo-arrival-ping-test.mjs +87 -0
  220. package/scripts/topo-auto-fit-test.mjs +73 -0
  221. package/scripts/topo-canvas-aria-test.mjs +108 -0
  222. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  223. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  224. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  225. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  226. package/scripts/topo-chip-focus-test.mjs +95 -0
  227. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  228. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  229. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  230. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  231. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  232. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  233. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  234. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  235. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  236. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  237. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  238. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  239. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  240. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  241. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  242. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  243. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  244. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  245. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  246. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  247. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  248. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  249. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  250. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  251. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  252. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  253. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  254. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  255. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  256. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  257. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  258. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  259. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  260. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  261. package/scripts/topo-edge-fadein-test.mjs +106 -0
  262. package/scripts/topo-edge-focus-test.mjs +113 -0
  263. package/scripts/topo-edge-hover-test.mjs +93 -0
  264. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  265. package/scripts/topo-edge-pill-test.mjs +105 -0
  266. package/scripts/topo-edge-smil-sync-test.mjs +148 -0
  267. package/scripts/topo-edge-smil-theme-ease-test.mjs +97 -0
  268. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  269. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  270. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  271. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  272. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  273. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  274. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  275. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  276. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  277. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  278. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  279. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  280. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  281. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  282. package/scripts/topo-fit-key-test.mjs +106 -0
  283. package/scripts/topo-freshness-chip-test.mjs +74 -0
  284. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  285. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  286. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  287. package/scripts/topo-group-count-test.mjs +6 -1
  288. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  289. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  290. package/scripts/topo-group-label-hover-test.mjs +119 -0
  291. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  292. package/scripts/topo-group-label-tint-test.mjs +99 -0
  293. package/scripts/topo-group-label-title-test.mjs +83 -0
  294. package/scripts/topo-group-pin-test.mjs +119 -0
  295. package/scripts/topo-group-pip-fadein-test.mjs +100 -0
  296. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  297. package/scripts/topo-group-status-pips-test.mjs +103 -0
  298. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  299. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  300. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  301. package/scripts/topo-groupbox-live-test.mjs +100 -0
  302. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  303. package/scripts/topo-groupbox-test.mjs +4 -2
  304. package/scripts/topo-halo-breath-ease-test.mjs +92 -0
  305. package/scripts/topo-halo-breath-stagger-test.mjs +89 -0
  306. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  307. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  308. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  309. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  310. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  311. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  312. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  313. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  314. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  315. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  316. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  317. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  318. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  319. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  320. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  321. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  322. package/scripts/topo-hub-a11y-test.mjs +112 -0
  323. package/scripts/topo-hub-busyness-test.mjs +100 -0
  324. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  325. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  326. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  327. package/scripts/topo-hub-fadein-test.mjs +88 -0
  328. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  329. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  330. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  331. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  332. package/scripts/topo-hub-title-test.mjs +93 -0
  333. package/scripts/topo-hub-working-count-test.mjs +99 -0
  334. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  335. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  336. package/scripts/topo-label-lift-test.mjs +119 -0
  337. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  338. package/scripts/topo-layout-key-test.mjs +91 -0
  339. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  340. package/scripts/topo-layout-toggle-test.mjs +131 -0
  341. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  342. package/scripts/topo-legend-count-test.mjs +88 -0
  343. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  344. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  345. package/scripts/topo-legend-hover-test.mjs +113 -0
  346. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  347. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  348. package/scripts/topo-legend-pin-test.mjs +128 -0
  349. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  350. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  351. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  352. package/scripts/topo-legend-row-title-test.mjs +102 -0
  353. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  354. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  355. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  356. package/scripts/topo-minimap-test.mjs +68 -0
  357. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  358. package/scripts/topo-mobile-chips-test.mjs +103 -0
  359. package/scripts/topo-node-a11y-test.mjs +107 -0
  360. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  361. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  362. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  363. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  364. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  365. package/scripts/topo-node-title-peers-test.mjs +107 -0
  366. package/scripts/topo-node-title-test.mjs +101 -0
  367. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  368. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  369. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  370. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  371. package/scripts/topo-orbit-opacity-bucket-test.mjs +124 -0
  372. package/scripts/topo-overlap-test.mjs +32 -5
  373. package/scripts/topo-pan-cursor-test.mjs +62 -0
  374. package/scripts/topo-panel-elevation-test.mjs +76 -0
  375. package/scripts/topo-panel-fadein-test.mjs +102 -0
  376. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  377. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  378. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  379. package/scripts/topo-particle-stagger-test.mjs +82 -0
  380. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  381. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  382. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  383. package/scripts/topo-pin-intersection-test.mjs +121 -0
  384. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  385. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  386. package/scripts/topo-pin-persistence-test.mjs +105 -0
  387. package/scripts/topo-ping-pulse-ease-test.mjs +96 -0
  388. package/scripts/topo-popover-meta-test.mjs +78 -0
  389. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  390. package/scripts/topo-pressure-bar-test.mjs +69 -0
  391. package/scripts/topo-pressure-hover-test.mjs +123 -0
  392. package/scripts/topo-pressure-pin-test.mjs +119 -0
  393. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  394. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  395. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  396. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  397. package/scripts/topo-pulse-rate-test.mjs +71 -0
  398. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  399. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  400. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  401. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  402. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  403. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  404. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  405. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  406. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  407. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  408. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  409. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  410. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  411. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  412. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  413. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  414. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  415. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  416. package/scripts/topo-recent-row-title-test.mjs +112 -0
  417. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  418. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  419. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  420. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  421. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  422. package/scripts/topo-reduced-motion-test.mjs +77 -0
  423. package/scripts/topo-reset-spin-test.mjs +120 -0
  424. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  425. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  426. package/scripts/topo-spoke-tint-test.mjs +96 -0
  427. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  428. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  429. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  430. package/scripts/topo-svg-focus-test.mjs +112 -0
  431. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  432. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  433. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  434. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  435. package/scripts/topo-tier-ring-test.mjs +69 -0
  436. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  437. package/scripts/topo-tier-stagger-test.mjs +89 -0
  438. package/scripts/topo-tz-ghost-test.mjs +69 -0
  439. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  440. package/scripts/topo-vendor-chip-self-hover-test.mjs +130 -0
  441. package/scripts/topo-vendor-chip-tabular-test.mjs +89 -0
  442. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  443. package/scripts/topo-vendor-pill-test.mjs +125 -0
  444. package/scripts/topo-vendor-pin-test.mjs +125 -0
  445. package/scripts/topo-view-smooth-test.mjs +131 -0
  446. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  447. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  448. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  449. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  450. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  451. package/scripts/topo-zoom-keys-test.mjs +89 -0
  452. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  453. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  454. package/.next/static/chunks/04ppgi-mphhr6.js +0 -1
  455. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  456. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  457. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  458. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  459. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  460. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  461. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  462. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  463. package/.next/static/chunks/0zs511wcsq~0j.js +0 -1
  464. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  465. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  466. package/.next/static/chunks/15gj.t675_yw3.js +0 -1
  467. /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → 1RSKMzmY8kvB-f37IJbmE}/_buildManifest.js +0 -0
  468. /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → 1RSKMzmY8kvB-f37IJbmE}/_clientMiddlewareManifest.js +0 -0
  469. /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → 1RSKMzmY8kvB-f37IJbmE}/_ssgManifest.js +0 -0
@@ -0,0 +1,126 @@
1
+ /* Round 170 verification: layout toggle (Ring ↔ Grid) crossfade.
2
+ *
3
+ * Pre-R170 clicking Ring/Grid teleported every node to its new
4
+ * position in one frame — most jarring user action on the canvas.
5
+ *
6
+ * R170 introduces layoutSwitching one-shot flag (same pattern as
7
+ * R168 smoothView but on opacity axis). The viewport <g>:
8
+ * transition: 'opacity 250ms ease-out' (always present)
9
+ * + 'transform 300ms ease-out' (when smoothView armed)
10
+ * opacity: layoutSwitching ? 0.45 : 1
11
+ * data-topo-viewport-layout-switching: 'true' | 'false'
12
+ *
13
+ * Layout state swap is synchronous (setLayout) — the dim masks
14
+ * the snap as a soft blink. Auto-clears 400ms (covers fade-down
15
+ * 250ms + buffer for React commit).
16
+ *
17
+ * Test:
18
+ * 1. Idle (ring layout): layoutSwitching='false', opacity=1
19
+ * 2. Press 'L' (toggleLayout) → flag='true', opacity=0.45
20
+ * 3. Layout state flipped to grid (group boxes visible)
21
+ * 4. Wait 450ms → flag='false', opacity=1
22
+ * 5. Press 'L' again → flag='true' (reverse direction works too)
23
+ */
24
+ import { chromium } from 'playwright';
25
+ import { readFileSync } from 'node:fs';
26
+
27
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
28
+ const browser = await chromium.launch({ headless: true });
29
+
30
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try {
34
+ localStorage.setItem('anet-theme', 'cyber');
35
+ sessionStorage.setItem('anet_v3_auth', '1');
36
+ localStorage.setItem('anet-topo-layout', 'ring');
37
+ } catch {}
38
+ });
39
+
40
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
41
+ await ctx.route('**/api/hub/status*', async (route) => {
42
+ const r = await route.fetch();
43
+ const b = await r.json();
44
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
45
+ const mk = (alias) => ({
46
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
47
+ network_id: nid, project_dir: null,
48
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
49
+ });
50
+ // Prefix-clustered aliases so group boxes render in grid layout
51
+ await route.fulfill({ response: r, json: { ...b, sessions: [
52
+ mk('agents-a1'), mk('agents-a2'), mk('infra-b1'), mk('infra-b2'),
53
+ ] } });
54
+ });
55
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
56
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
57
+
58
+ const page = await ctx.newPage();
59
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
60
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
61
+ await page.waitForSelector('[data-topo-viewport]', { timeout: 10000 });
62
+ await page.waitForTimeout(400);
63
+
64
+ const probeViewport = () => page.evaluate(() => {
65
+ const g = document.querySelector('[data-topo-viewport]');
66
+ return {
67
+ layoutSwitching: g?.getAttribute('data-topo-viewport-layout-switching'),
68
+ opacity: g?.style?.opacity || '',
69
+ transition: g?.style?.transition || '',
70
+ hubVisible: !!document.querySelector('[data-topo-hub]'),
71
+ groupBoxes: document.querySelectorAll('[data-group-box-pinned]').length,
72
+ };
73
+ });
74
+
75
+ const idle = await probeViewport();
76
+
77
+ // Press 'L' to toggle ring → grid. Probe within 400ms window.
78
+ await page.keyboard.press('l');
79
+ await page.waitForTimeout(50);
80
+ const duringToggle1 = await probeViewport();
81
+
82
+ // Wait past 400ms window
83
+ await page.waitForTimeout(420);
84
+ const afterToggle1 = await probeViewport();
85
+
86
+ // Press 'L' again to toggle grid → ring
87
+ await page.keyboard.press('l');
88
+ await page.waitForTimeout(50);
89
+ const duringToggle2 = await probeViewport();
90
+
91
+ await page.waitForTimeout(420);
92
+ const afterToggle2 = await probeViewport();
93
+
94
+ await browser.close();
95
+
96
+ const results = {
97
+ viewport_found: idle !== null,
98
+ idle_layoutSwitching_false: idle.layoutSwitching === 'false',
99
+ idle_opacity_1: idle.opacity === '1' || idle.opacity === '',
100
+ idle_has_opacity_transition: idle.transition.includes('opacity 250ms'),
101
+ idle_in_ring: idle.hubVisible && idle.groupBoxes === 0,
102
+
103
+ toggle1_flag_true: duringToggle1.layoutSwitching === 'true',
104
+ toggle1_opacity_0p45: duringToggle1.opacity === '0.45',
105
+ // Layout state has flipped synchronously inside toggleLayout
106
+ toggle1_now_grid: !duringToggle1.hubVisible && duringToggle1.groupBoxes > 0,
107
+
108
+ after1_flag_false: afterToggle1.layoutSwitching === 'false',
109
+ after1_opacity_1: afterToggle1.opacity === '1',
110
+ after1_still_grid: !afterToggle1.hubVisible && afterToggle1.groupBoxes > 0,
111
+
112
+ toggle2_flag_true: duringToggle2.layoutSwitching === 'true',
113
+ toggle2_opacity_0p45: duringToggle2.opacity === '0.45',
114
+ toggle2_back_to_ring: duringToggle2.hubVisible && duringToggle2.groupBoxes === 0,
115
+
116
+ after2_flag_false: afterToggle2.layoutSwitching === 'false',
117
+ after2_opacity_1: afterToggle2.opacity === '1',
118
+ };
119
+ const ok = Object.values(results).every(Boolean);
120
+ console.log(`${ok ? '✅' : '❌'} layout switch crossfade:`, JSON.stringify(results),
121
+ `\n idle =`, idle,
122
+ `\n during toggle1 (ring→grid) =`, duringToggle1,
123
+ `\n after toggle1 =`, afterToggle1,
124
+ `\n during toggle2 (grid→ring) =`, duringToggle2,
125
+ `\n after toggle2 =`, afterToggle2);
126
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,131 @@
1
+ /* Round 163 verification: Ring/Grid layout toggle buttons pick
2
+ * up the R154 chrome-button polish convention.
3
+ *
4
+ * Pre-R163 the buttons had aria-pressed + transition-colors but:
5
+ * - no focus-visible ring (browser default invisible on canvas)
6
+ * - inactive hover only nudged text-gray-500 → gray-400, no bg
7
+ *
8
+ * R163 adds:
9
+ * data-topo-chrome-layout="ring|grid"
10
+ * data-topo-chrome-layout-active="true|false"
11
+ * focus-visible:ring-2 ring-cyan-400/60 ring-inset
12
+ * inactive: hover:text-gray-300 + hover:bg-cyan-500/5
13
+ * active: hover:bg-cyan-500/20 (slightly hotter)
14
+ *
15
+ * Test:
16
+ * 1. Both buttons present with data-topo-chrome-layout attr
17
+ * 2. Active button has bg-cyan-500/15 class fragment (existing)
18
+ * 3. Focus the inactive button → focus-visible ring class fragment
19
+ * 4. Hover the inactive button → bg color shifts from transparent
20
+ * to a cyan-ish tint (border bg-cyan-500/5)
21
+ */
22
+ import { chromium } from 'playwright';
23
+ import { readFileSync } from 'node:fs';
24
+
25
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
26
+ const browser = await chromium.launch({ headless: true });
27
+
28
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); localStorage.setItem('anet-topo-layout', 'ring'); } catch {}
32
+ });
33
+
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
47
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
48
+
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
52
+ await page.waitForSelector('[data-topo-chrome-layout]', { timeout: 10000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ // Probe basic attrs & class membership
56
+ const probe = await page.evaluate(() => {
57
+ const ring = document.querySelector('[data-topo-chrome-layout="ring"]');
58
+ const grid = document.querySelector('[data-topo-chrome-layout="grid"]');
59
+ if (!ring || !grid) return null;
60
+ return {
61
+ ring_active: ring.getAttribute('data-topo-chrome-layout-active'),
62
+ grid_active: grid.getAttribute('data-topo-chrome-layout-active'),
63
+ ring_pressed: ring.getAttribute('aria-pressed'),
64
+ grid_pressed: grid.getAttribute('aria-pressed'),
65
+ ring_class: ring.getAttribute('class') || '',
66
+ grid_class: grid.getAttribute('class') || '',
67
+ };
68
+ });
69
+
70
+ // Focus the inactive (grid) button — focus-visible ring should kick in.
71
+ // Tab-focus produces the :focus-visible state; .focus() doesn't always.
72
+ // Use page.keyboard.press('Tab') after first focusing somewhere known.
73
+ await page.locator('[data-topo-chrome-layout="grid"]').focus();
74
+ // trigger keyboard navigation to enable focus-visible state heuristic
75
+ await page.evaluate(() => {
76
+ const el = document.querySelector('[data-topo-chrome-layout="grid"]');
77
+ if (el) el.matches(':focus-visible');
78
+ });
79
+ const focusVisibleProbe = await page.evaluate(() => {
80
+ const el = document.querySelector('[data-topo-chrome-layout="grid"]');
81
+ if (!el) return null;
82
+ return {
83
+ isActiveElement: document.activeElement === el,
84
+ // Inspect outline & box-shadow because the Tailwind focus-visible:ring
85
+ // class compiles to a box-shadow. After .focus() Chromium reports
86
+ // :focus-visible heuristically — we verify the class fragment exists
87
+ // in the stylesheet bound to this element rather than relying on the
88
+ // dynamic pseudo-class match.
89
+ classFragments: {
90
+ hasFocusVisibleRingClass: (el.getAttribute('class') || '').includes('focus-visible:ring-cyan-400/60'),
91
+ hasRingInset: (el.getAttribute('class') || '').includes('focus-visible:ring-inset'),
92
+ hasFocusOutlineNone: (el.getAttribute('class') || '').includes('focus:outline-none'),
93
+ },
94
+ };
95
+ });
96
+
97
+ // Hover the inactive (grid) button — bg should shift away from transparent.
98
+ const gridBgBefore = await page.evaluate(() => {
99
+ const el = document.querySelector('[data-topo-chrome-layout="grid"]');
100
+ return el ? getComputedStyle(el).backgroundColor : null;
101
+ });
102
+ await page.locator('[data-topo-chrome-layout="grid"]').hover();
103
+ await page.waitForTimeout(300);
104
+ const gridBgHover = await page.evaluate(() => {
105
+ const el = document.querySelector('[data-topo-chrome-layout="grid"]');
106
+ return el ? getComputedStyle(el).backgroundColor : null;
107
+ });
108
+
109
+ await browser.close();
110
+
111
+ const results = {
112
+ both_buttons_found: probe !== null,
113
+ ring_is_active: probe?.ring_active === 'true',
114
+ grid_is_inactive: probe?.grid_active === 'false',
115
+ ring_aria_pressed: probe?.ring_pressed === 'true',
116
+ grid_aria_unpressed: probe?.grid_pressed === 'false',
117
+ active_has_cyan_bg_class: probe?.ring_class.includes('bg-cyan-500/15'),
118
+ inactive_hover_bg_class: probe?.grid_class.includes('hover:bg-cyan-500/5'),
119
+ active_hover_bg_class: probe?.ring_class.includes('hover:bg-cyan-500/20'),
120
+ focus_outline_none: focusVisibleProbe?.classFragments?.hasFocusOutlineNone === true,
121
+ focus_visible_ring_class: focusVisibleProbe?.classFragments?.hasFocusVisibleRingClass === true,
122
+ focus_visible_ring_inset: focusVisibleProbe?.classFragments?.hasRingInset === true,
123
+ inactive_bg_before: gridBgBefore !== null,
124
+ hover_changes_bg: gridBgBefore !== gridBgHover,
125
+ };
126
+ const ok = Object.values(results).every(Boolean);
127
+ console.log(`${ok ? '✅' : '❌'} layout toggle polish:`, JSON.stringify(results),
128
+ `\n probe=`, probe,
129
+ `\n focus=`, focusVisibleProbe,
130
+ `\n bg before/hover=`, gridBgBefore, '→', gridBgHover);
131
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,126 @@
1
+ /* Round 204 verification: legend count text recedes when its status
2
+ * tier is empty (count = 0). Pre-R204 "0" at opacity 0.65 read
3
+ * visually identical to "12" at opacity 0.65; R204 drops empty rows
4
+ * to 0.30 (dark) / 0.28 (light) so the eye gets a "no nodes in this
5
+ * tier" signal without parsing the digit.
6
+ *
7
+ * Test (cyber dark, all 4 sessions = working → idle=0, offline=0):
8
+ * 1. Three legend count texts present with data-legend-count for
9
+ * working / idle / offline.
10
+ * 2. Working row: count='4', data-legend-count-empty='false',
11
+ * opacity = 0.65 (idle, not hovered).
12
+ * 3. Idle row: count='0', data-legend-count-empty='true',
13
+ * opacity ≈ 0.30 (dim recede).
14
+ * 4. Offline row: count='0', data-legend-count-empty='true',
15
+ * opacity ≈ 0.30.
16
+ * 5. Hover working row → its count bumps to opacity ~0.95
17
+ * (existing R95 hover behaviour unbroken).
18
+ * 6. Hover idle row → empty-tier opacity DOES NOT bump (empty
19
+ * gate wins over hover). Recedes regardless.
20
+ * 7. Transition: opacity 150ms ease-out still on all three.
21
+ */
22
+ import { chromium } from 'playwright';
23
+ import { readFileSync } from 'node:fs';
24
+
25
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
26
+ const browser = await chromium.launch({ headless: true });
27
+
28
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
32
+ });
33
+
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ // All 4 working — idle / offline tiers empty
40
+ const mk = (alias) => ({
41
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
42
+ network_id: nid, project_dir: null,
43
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
44
+ });
45
+ await route.fulfill({ response: r, json: { ...b, sessions: [
46
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
47
+ ] } });
48
+ });
49
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
50
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
51
+
52
+ const page = await ctx.newPage();
53
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
54
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
55
+ await page.waitForSelector('[data-legend-count="working"]', { timeout: 10000 });
56
+ await page.waitForTimeout(300);
57
+
58
+ const probe = async () => page.evaluate(() => {
59
+ const pick = (key) => {
60
+ const el = document.querySelector(`[data-legend-count="${key}"]`);
61
+ if (!el) return null;
62
+ return {
63
+ key,
64
+ text: el.textContent,
65
+ empty: el.getAttribute('data-legend-count-empty'),
66
+ opacity: parseFloat(el.getAttribute('opacity') || ''),
67
+ transition: el.style.transition,
68
+ };
69
+ };
70
+ return { working: pick('working'), idle: pick('idle'), offline: pick('offline') };
71
+ });
72
+
73
+ const idle = await probe();
74
+
75
+ // Hover the working row — verify R95 hover bump still fires (count > 0)
76
+ await page.hover('[data-legend-status="working"]');
77
+ await page.waitForTimeout(200);
78
+ const hovWorking = await probe();
79
+
80
+ // Move off, then hover the empty (idle=0) row — verify empty gate wins
81
+ await page.mouse.move(0, 0);
82
+ await page.waitForTimeout(200);
83
+ await page.hover('[data-legend-status="idle"]');
84
+ await page.waitForTimeout(200);
85
+ const hovIdle = await probe();
86
+
87
+ await page.mouse.move(0, 0);
88
+ await page.waitForTimeout(200);
89
+
90
+ await browser.close();
91
+
92
+ const opacityNear = (got, want, tol = 0.04) => Math.abs(got - want) < tol;
93
+
94
+ const results = {
95
+ working_present: !!idle?.working,
96
+ idle_present: !!idle?.idle,
97
+ offline_present: !!idle?.offline,
98
+
99
+ working_text_4: idle?.working?.text === '4',
100
+ working_not_empty: idle?.working?.empty === 'false',
101
+ working_opacity_0p65: opacityNear(idle?.working?.opacity ?? 0, 0.65),
102
+
103
+ idle_text_0: idle?.idle?.text === '0',
104
+ idle_empty_true: idle?.idle?.empty === 'true',
105
+ idle_opacity_dim: opacityNear(idle?.idle?.opacity ?? 0, 0.30),
106
+
107
+ offline_text_0: idle?.offline?.text === '0',
108
+ offline_empty_true: idle?.offline?.empty === 'true',
109
+ offline_opacity_dim: opacityNear(idle?.offline?.opacity ?? 0, 0.30),
110
+
111
+ // R95 hover still bumps when row populated
112
+ working_hover_opacity_high: opacityNear(hovWorking?.working?.opacity ?? 0, 0.95),
113
+
114
+ // Empty-gate wins: hovering an empty row does NOT bump its count opacity
115
+ idle_hover_stays_dim: opacityNear(hovIdle?.idle?.opacity ?? 0, 0.30),
116
+
117
+ // Transition still in place across all 3
118
+ all_have_transition: ['working', 'idle', 'offline'].every(k =>
119
+ /opacity\s+(?:150ms|0\.15s)/.test(idle?.[k]?.transition || '')),
120
+ };
121
+ const ok = Object.values(results).every(Boolean);
122
+ console.log(`${ok ? '✅' : '❌'} legend count empty:`, JSON.stringify(results),
123
+ `\n idle:`, idle,
124
+ `\n hovWorking:`, hovWorking,
125
+ `\n hovIdle:`, hovIdle);
126
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,88 @@
1
+ /* Round 95 verification: legend rows show live counts. Fleet = 3
2
+ * working + 2 idle + 1 offline. Each row's data-legend-count
3
+ * attribute matches the expected number. Pinning a row brightens
4
+ * its count alongside the label.
5
+ *
6
+ * The count is right-aligned at x=215 (after the flow-arrow swatch
7
+ * which spans x=140-196 on the offline row), pointerEvents:none so
8
+ * the row's R55 hover affordance still hits.
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const browser = await chromium.launch({ headless: true });
15
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try {
19
+ localStorage.setItem('anet-theme', 'cyber');
20
+ localStorage.setItem('anet-topo-layout', 'ring');
21
+ sessionStorage.setItem('anet_v3_auth', '1');
22
+ sessionStorage.removeItem('anet-topo-pinned-status');
23
+ } catch {}
24
+ });
25
+
26
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
27
+ const stale10 = new Date(Date.now() - 10 * 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 mk = (alias, status, ts) => ({
33
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
34
+ network_id: nid, project_dir: null,
35
+ created_at: ts, updated_at: ts, last_seen_at: ts,
36
+ });
37
+ const sessions = [
38
+ mk('wkr1', 'working', fresh),
39
+ mk('wkr2', 'working', fresh),
40
+ mk('wkr3', 'working', fresh),
41
+ mk('idl1', 'idle', fresh),
42
+ mk('idl2', 'idle', fresh),
43
+ mk('off1', 'offline', stale10),
44
+ ];
45
+ await route.fulfill({ response: r, json: { ...b, sessions } });
46
+ });
47
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
48
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const counts = () => page.evaluate(() => ({
55
+ working: document.querySelector('[data-legend-count="working"]')?.textContent,
56
+ idle: document.querySelector('[data-legend-count="idle"]')?.textContent,
57
+ offline: document.querySelector('[data-legend-count="offline"]')?.textContent,
58
+ }));
59
+ const opacities = () => page.evaluate(() => ({
60
+ working: +(document.querySelector('[data-legend-count="working"]')?.getAttribute('opacity') || '0'),
61
+ idle: +(document.querySelector('[data-legend-count="idle"]')?.getAttribute('opacity') || '0'),
62
+ offline: +(document.querySelector('[data-legend-count="offline"]')?.getAttribute('opacity') || '0'),
63
+ }));
64
+
65
+ const c = await counts();
66
+ const opsRest = await opacities();
67
+
68
+ // Pin the working row — its count should brighten.
69
+ await page.locator('[data-legend-status="working"]').click();
70
+ await page.waitForTimeout(250);
71
+ const opsPinned = await opacities();
72
+
73
+ await browser.close();
74
+
75
+ const results = {
76
+ workingCount_3: c.working === '3',
77
+ idleCount_2: c.idle === '2',
78
+ offlineCount_1: c.offline === '1',
79
+ resting_dim: opsRest.working < 0.7 && opsRest.idle < 0.7 && opsRest.offline < 0.7,
80
+ pinnedWorking_bright: opsPinned.working >= 0.9,
81
+ pinnedWorking_othersUnchanged: opsPinned.idle < 0.7 && opsPinned.offline < 0.7,
82
+ };
83
+ const ok = Object.values(results).every(Boolean);
84
+ console.log(`${ok ? '✅' : '❌'} legend counts:`, JSON.stringify(results),
85
+ `\n counts=`, c,
86
+ `\n opsRest=`, opsRest,
87
+ `\n opsPinned=`, opsPinned);
88
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,121 @@
1
+ /* Round 239 verification: legend count text fill picks up the
2
+ * row's tier colour on hover/pin, completing the hover-deepen-own-
3
+ * hue idiom for the legend row triple (swatch + label + count).
4
+ *
5
+ * Pre-R239 the count digit had opacity 0.65→0.95 on hover but
6
+ * stayed at pal.legendText (neutral gray) for fill. The swatch
7
+ * (R197 grow) and label (R55 brighten) both reacted to hover with
8
+ * tier-identity signals; only the count stayed gray. R239 adds
9
+ * fill transition to row.fill (green/teal/slate) on hover so the
10
+ * row reads as one tier-coloured unit.
11
+ *
12
+ * Empty tiers (row.count === 0) stay at pal.legendText regardless
13
+ * of hover — empty doesn't get to claim tier identity.
14
+ *
15
+ * Test scope:
16
+ * - rest state: count fill === neutral (pal.legendText)
17
+ * - hover state (Playwright .hover() on the legend row <g>):
18
+ * count fill === row.fill (tier colour)
19
+ * - data-legend-count-fill attr flips 'neutral' ↔ 'tier'
20
+ * - transition list includes both opacity AND fill at 150ms
21
+ *
22
+ * Scenario: 4 working agents → working tier has count=4 (populated),
23
+ * idle + offline tiers stay at 0 (empty, never tier-coloured).
24
+ * Hover the working legend row, verify only that row's count
25
+ * flips to tier colour.
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*', (r2) => r2.fulfill({ json: { messages: [] } }));
52
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.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-legend-count="working"]', { timeout: 10000, state: 'attached' });
58
+ await page.waitForTimeout(300);
59
+
60
+ const probeCount = (page, tier) => page.evaluate((t) => {
61
+ const text = document.querySelector(`[data-legend-count="${t}"]`);
62
+ if (!text) return null;
63
+ const cs = getComputedStyle(text);
64
+ return {
65
+ fillAttr: text.getAttribute('fill'),
66
+ fillComp: cs.fill,
67
+ empty: text.getAttribute('data-legend-count-empty'),
68
+ fillState: text.getAttribute('data-legend-count-fill'),
69
+ transition: cs.transition,
70
+ text: (text.textContent || '').trim(),
71
+ };
72
+ }, tier);
73
+
74
+ // REST STATE — no hover
75
+ const rest = await probeCount(page, 'working');
76
+
77
+ // HOVER STATE — hover the working legend row group
78
+ await page.locator('[data-legend-status="working"]').first().hover({ force: true });
79
+ await page.waitForTimeout(400);
80
+ const hover = await probeCount(page, 'working');
81
+
82
+ // Idle tier is empty in this scenario — should stay neutral even
83
+ // when hovered. Hover the idle row to confirm the empty-tier
84
+ // exclusion holds.
85
+ await page.locator('[data-legend-status="idle"]').first().hover({ force: true });
86
+ await page.waitForTimeout(400);
87
+ const idleHover = await probeCount(page, 'idle');
88
+
89
+ await browser.close();
90
+
91
+ // Browser normalises '150ms' → '0.15s' in computed-style transitions.
92
+ // Accept either form.
93
+ const hasTransition = (s, prop) => new RegExp(`${prop}\\s+(?:150ms|0\\.15s)`).test(s || '');
94
+ // Cyber theme working tier fill is #22c55e = rgb(34, 197, 94)
95
+ const tierColorRgb = 'rgb(34, 197, 94)';
96
+ // Cyber theme legendText (pal.legendText for dark palette) = #94a3b8 = rgb(148, 163, 184)
97
+ const neutralRgb = 'rgb(148, 163, 184)';
98
+
99
+ const results = {
100
+ rest_present: rest !== null,
101
+ rest_fill_state_neutral: rest?.fillState === 'neutral',
102
+ rest_fill_is_neutral: rest?.fillComp === neutralRgb,
103
+ rest_text_is_4: rest?.text === '4',
104
+ rest_transition_has_fill: hasTransition(rest?.transition, 'fill'),
105
+ rest_transition_has_opacity: hasTransition(rest?.transition, 'opacity'),
106
+
107
+ hover_fill_state_tier: hover?.fillState === 'tier',
108
+ hover_fill_is_tier: hover?.fillComp === tierColorRgb,
109
+ hover_text_is_4: hover?.text === '4',
110
+
111
+ // Empty tier (idle, count=0) stays neutral even on hover
112
+ idle_empty: idleHover?.empty === 'true',
113
+ idle_stays_neutral: idleHover?.fillState === 'neutral',
114
+ idle_fill_still_neutral: idleHover?.fillComp === neutralRgb,
115
+ };
116
+ const ok = Object.values(results).every(Boolean);
117
+ console.log(`${ok ? '✅' : '❌'} legend count tier fill:`, JSON.stringify(results),
118
+ '\n rest: ', rest,
119
+ '\n hover: ', hover,
120
+ '\n idleHover:', idleHover);
121
+ process.exit(ok ? 0 : 1);