@sleep2agi/agent-network-dashboard 0.4.6-preview.21 → 0.4.6-preview.211

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 (414) 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/09mo5s0d1t.rl.js +4 -0
  186. package/.next/static/chunks/0_tufsuno-ybu.js +1 -0
  187. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  188. package/.next/static/chunks/0glmrdj9112tc.js +7 -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/0yjst6n4eqy4c.js +1 -0
  192. package/.next/static/chunks/0~k1yvhakbob6.css +2 -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 +4228 -255
  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 +1 -1
  208. package/scripts/help-overlay-topo-test.mjs +44 -0
  209. package/scripts/servers-drawer-live-test.mjs +96 -0
  210. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  211. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  212. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  213. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  216. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  217. package/scripts/topo-arrival-ping-test.mjs +87 -0
  218. package/scripts/topo-arrow-tier-test.mjs +61 -0
  219. package/scripts/topo-auto-fit-test.mjs +73 -0
  220. package/scripts/topo-canvas-aria-test.mjs +108 -0
  221. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  222. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  223. package/scripts/topo-chip-focus-test.mjs +95 -0
  224. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  225. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  226. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  227. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  228. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  229. package/scripts/topo-click-ripple-test.mjs +66 -0
  230. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  231. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  232. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  233. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  234. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  235. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  236. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  237. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  238. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  239. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  240. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  241. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  242. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  243. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  244. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  245. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  246. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  247. package/scripts/topo-edge-fadein-test.mjs +106 -0
  248. package/scripts/topo-edge-focus-test.mjs +113 -0
  249. package/scripts/topo-edge-hover-test.mjs +93 -0
  250. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  251. package/scripts/topo-edge-pill-test.mjs +105 -0
  252. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  253. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  254. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  255. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  256. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  257. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  258. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  259. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  260. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  261. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  262. package/scripts/topo-fit-key-test.mjs +106 -0
  263. package/scripts/topo-freshness-chip-test.mjs +74 -0
  264. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  265. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  266. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  267. package/scripts/topo-group-count-test.mjs +68 -0
  268. package/scripts/topo-group-hover-test.mjs +82 -0
  269. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  270. package/scripts/topo-group-label-hover-test.mjs +119 -0
  271. package/scripts/topo-group-label-tint-test.mjs +99 -0
  272. package/scripts/topo-group-label-title-test.mjs +83 -0
  273. package/scripts/topo-group-pin-test.mjs +119 -0
  274. package/scripts/topo-group-status-pips-test.mjs +103 -0
  275. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  276. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  277. package/scripts/topo-groupbox-live-test.mjs +100 -0
  278. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  279. package/scripts/topo-groupbox-test.mjs +4 -2
  280. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  281. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  282. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  283. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  284. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  285. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  286. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  287. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  288. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  289. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  290. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  291. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  292. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  293. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  294. package/scripts/topo-hub-a11y-test.mjs +112 -0
  295. package/scripts/topo-hub-busyness-test.mjs +100 -0
  296. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  297. package/scripts/topo-hub-fadein-test.mjs +88 -0
  298. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  299. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  300. package/scripts/topo-hub-title-test.mjs +93 -0
  301. package/scripts/topo-hub-working-count-test.mjs +99 -0
  302. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  303. package/scripts/topo-label-lift-test.mjs +119 -0
  304. package/scripts/topo-label-scale-test.mjs +78 -0
  305. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  306. package/scripts/topo-layout-key-test.mjs +91 -0
  307. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  308. package/scripts/topo-layout-toggle-test.mjs +131 -0
  309. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  310. package/scripts/topo-legend-count-test.mjs +88 -0
  311. package/scripts/topo-legend-hover-test.mjs +113 -0
  312. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  313. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  314. package/scripts/topo-legend-pin-test.mjs +128 -0
  315. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  316. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  317. package/scripts/topo-legend-row-title-test.mjs +102 -0
  318. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  319. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  320. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  321. package/scripts/topo-minimap-test.mjs +68 -0
  322. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  323. package/scripts/topo-mobile-chips-test.mjs +103 -0
  324. package/scripts/topo-node-a11y-test.mjs +107 -0
  325. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  326. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  327. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  328. package/scripts/topo-node-title-peers-test.mjs +107 -0
  329. package/scripts/topo-node-title-test.mjs +101 -0
  330. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  331. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  332. package/scripts/topo-offline-dim-test.mjs +70 -0
  333. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  334. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  335. package/scripts/topo-overlap-test.mjs +32 -5
  336. package/scripts/topo-pan-cursor-test.mjs +62 -0
  337. package/scripts/topo-panel-elevation-test.mjs +76 -0
  338. package/scripts/topo-panel-fadein-test.mjs +102 -0
  339. package/scripts/topo-panel-hot-tail-fade-test.mjs +124 -0
  340. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  341. package/scripts/topo-particle-stagger-test.mjs +82 -0
  342. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  343. package/scripts/topo-pin-intersection-test.mjs +121 -0
  344. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  345. package/scripts/topo-pin-persistence-test.mjs +105 -0
  346. package/scripts/topo-popover-meta-test.mjs +78 -0
  347. package/scripts/topo-pressure-bar-test.mjs +69 -0
  348. package/scripts/topo-pressure-hover-test.mjs +123 -0
  349. package/scripts/topo-pressure-pin-test.mjs +119 -0
  350. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  351. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  352. package/scripts/topo-pulse-rate-test.mjs +71 -0
  353. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  354. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  355. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  356. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  357. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  358. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  359. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  360. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  361. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  362. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  363. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  364. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  365. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  366. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  367. package/scripts/topo-recent-row-title-test.mjs +112 -0
  368. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  369. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  370. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  371. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  372. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  373. package/scripts/topo-reduced-motion-test.mjs +77 -0
  374. package/scripts/topo-reset-spin-test.mjs +120 -0
  375. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  376. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  377. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  378. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  379. package/scripts/topo-svg-focus-test.mjs +112 -0
  380. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  381. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  382. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  383. package/scripts/topo-tier-ring-test.mjs +69 -0
  384. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  385. package/scripts/topo-tier-stagger-test.mjs +89 -0
  386. package/scripts/topo-tz-ghost-test.mjs +69 -0
  387. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  388. package/scripts/topo-vendor-chip-self-hover-test.mjs +130 -0
  389. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  390. package/scripts/topo-vendor-pill-test.mjs +125 -0
  391. package/scripts/topo-vendor-pin-test.mjs +125 -0
  392. package/scripts/topo-view-smooth-test.mjs +131 -0
  393. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  394. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  395. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  396. package/scripts/topo-zoom-keys-test.mjs +89 -0
  397. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  398. package/.next/static/chunks/01j59m93~ijpq.js +0 -1
  399. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  400. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  401. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  402. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  403. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  404. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  405. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  406. package/.next/static/chunks/0ms~5~zq93-vs.js +0 -1
  407. package/.next/static/chunks/0nh4tljy5z5ns.js +0 -1
  408. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  409. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  410. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  411. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  412. /package/.next/static/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_buildManifest.js +0 -0
  413. /package/.next/static/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_clientMiddlewareManifest.js +0 -0
  414. /package/.next/static/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_ssgManifest.js +0 -0
@@ -0,0 +1,102 @@
1
+ /* Round 175 verification: corner panels fade-in after the
2
+ * canvas content reveal.
3
+ *
4
+ * Pre-R175 the recent-signal (top-left) + legend (top-right)
5
+ * panels appeared instantly while nodes/edges/group boxes/tier
6
+ * rings staggered in around them. The panels felt like 'already
7
+ * there' before the content showed up.
8
+ *
9
+ * R175 adds .anet-fade-in + 700ms / 800ms animation-delay on
10
+ * the two panel <g> wrappers — fifth surface in the first-paint
11
+ * reveal family (after R9 nodes / R172 edges / R173 group boxes
12
+ * / R174 tier rings).
13
+ *
14
+ * Test:
15
+ * 1. Mock 3 sessions in ring layout
16
+ * 2. Probe [data-topo-panel="recent"] and [="legend"]
17
+ * 3. Both have .anet-fade-in className
18
+ * 4. recent panel animationDelay = 700ms
19
+ * 5. legend panel animationDelay = 800ms
20
+ * 6. data-topo-panel-fade-delay attributes match
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 {
32
+ localStorage.setItem('anet-theme', 'cyber');
33
+ sessionStorage.setItem('anet_v3_auth', '1');
34
+ localStorage.setItem('anet-topo-layout', 'ring');
35
+ } catch {}
36
+ });
37
+
38
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
39
+ await ctx.route('**/api/hub/status*', async (route) => {
40
+ const r = await route.fetch();
41
+ const b = await r.json();
42
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
43
+ const mk = (alias) => ({
44
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
45
+ network_id: nid, project_dir: null,
46
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
47
+ });
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
49
+ });
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
51
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
52
+
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
56
+ await page.waitForSelector('[data-topo-panel="recent"]', { timeout: 10000 });
57
+ await page.waitForSelector('[data-topo-panel="legend"]', { timeout: 10000 });
58
+ await page.waitForTimeout(400);
59
+
60
+ const probe = await page.evaluate(() => {
61
+ const grab = (sel) => {
62
+ const el = document.querySelector(sel);
63
+ if (!el) return null;
64
+ return {
65
+ hasFadeClass: (el.getAttribute('class') || '').includes('anet-fade-in'),
66
+ fadeDelayAttr: parseFloat(el.getAttribute('data-topo-panel-fade-delay') || ''),
67
+ animationDelay: el.style.animationDelay || getComputedStyle(el).animationDelay,
68
+ };
69
+ };
70
+ return {
71
+ recent: grab('[data-topo-panel="recent"]'),
72
+ legend: grab('[data-topo-panel="legend"]'),
73
+ };
74
+ });
75
+
76
+ await browser.close();
77
+
78
+ const parseDelay = (s) => {
79
+ if (!s) return null;
80
+ if (s.endsWith('ms')) return parseFloat(s);
81
+ if (s.endsWith('s')) return parseFloat(s) * 1000;
82
+ return parseFloat(s);
83
+ };
84
+ const recentDelay = parseDelay(probe.recent?.animationDelay);
85
+ const legendDelay = parseDelay(probe.legend?.animationDelay);
86
+
87
+ const results = {
88
+ recent_panel_found: probe.recent !== null,
89
+ legend_panel_found: probe.legend !== null,
90
+ recent_has_fade_class: probe.recent?.hasFadeClass === true,
91
+ legend_has_fade_class: probe.legend?.hasFadeClass === true,
92
+ recent_delay_attr_700: probe.recent?.fadeDelayAttr === 700,
93
+ legend_delay_attr_800: probe.legend?.fadeDelayAttr === 800,
94
+ recent_anim_delay_700ms: recentDelay === 700,
95
+ legend_anim_delay_800ms: legendDelay === 800,
96
+ legend_lags_recent_100ms: legendDelay - recentDelay === 100,
97
+ };
98
+ const ok = Object.values(results).every(Boolean);
99
+ console.log(`${ok ? '✅' : '❌'} panel fade-in:`, JSON.stringify(results),
100
+ `\n recent =`, probe.recent, `delay=${recentDelay}`,
101
+ `\n legend =`, probe.legend, `delay=${legendDelay}`);
102
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,124 @@
1
+ /* Round 190 verification: R129 hot-tail tspan in recent-signal
2
+ * panel header gets anet-fade-in for smooth entrance.
3
+ *
4
+ * Pre-R190 the tspan mounted/unmounted on hotFlowCount > 0 —
5
+ * snap. R190 keeps the conditional render (so the tspan
6
+ * doesn't consume layout when no hot flows) but adds the R3
7
+ * .anet-fade-in CSS animation that plays once on mount.
8
+ *
9
+ * Test scenarios:
10
+ * A. 0 hot flows (just a few cold ones):
11
+ * [data-recent-panel-hot-count] NOT present
12
+ * B. 1 hot flow (count=12):
13
+ * tspan present with text '· 1 hot', className contains
14
+ * 'anet-fade-in', data-recent-panel-hot-count='1'
15
+ * C. 2 hot flows (count=15 + count=20):
16
+ * data-recent-panel-hot-count='2', text '· 2 hot'
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
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
28
+ });
29
+
30
+ async function run(scenario) {
31
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
32
+ await ctx.unroute('**/api/hub/status*').catch(() => {});
33
+ await ctx.unroute('**/api/hub/messages*').catch(() => {});
34
+ await ctx.unroute('**/api/hub/tasks*').catch(() => {});
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: [
45
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
46
+ ] } });
47
+ });
48
+ const now = Date.now();
49
+ const msgs = scenario(now);
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
51
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
52
+
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
56
+ await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
57
+ await page.waitForTimeout(400);
58
+
59
+ const out = await page.evaluate(() => {
60
+ const tail = document.querySelector('[data-recent-panel-hot-count]');
61
+ return {
62
+ tailPresent: !!tail,
63
+ tailText: tail?.textContent || null,
64
+ tailCount: tail?.getAttribute('data-recent-panel-hot-count'),
65
+ tailClassName: tail?.getAttribute('class') || '',
66
+ };
67
+ });
68
+ await page.close();
69
+ return out;
70
+ }
71
+
72
+ // Scenario A: 0 hot — 1 cold flow
73
+ const a = await run((now) => {
74
+ const m = [];
75
+ for (let i = 0; i < 4; i++) {
76
+ m.push({ id: `cold${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
77
+ network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString() });
78
+ }
79
+ return m;
80
+ });
81
+
82
+ // Scenario B: 1 hot flow alpha→beta count=12
83
+ const b = await run((now) => {
84
+ const m = [];
85
+ for (let i = 0; i < 12; i++) {
86
+ m.push({ 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
+ return m;
90
+ });
91
+
92
+ // Scenario C: 2 hot flows
93
+ const c = await run((now) => {
94
+ const m = [];
95
+ for (let i = 0; i < 15; i++) {
96
+ m.push({ id: `h1-${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
97
+ network_id: 'default', created_at: new Date(now - (5000 + i * 50)).toISOString() });
98
+ }
99
+ for (let i = 0; i < 20; i++) {
100
+ m.push({ id: `h2-${i}`, from_alias: 'gamma', to_alias: 'delta', content: 'hi',
101
+ network_id: 'default', created_at: new Date(now - (15000 + i * 50)).toISOString() });
102
+ }
103
+ return m;
104
+ });
105
+
106
+ await browser.close();
107
+
108
+ const results = {
109
+ a_no_tail: a.tailPresent === false,
110
+ b_tail_present: b.tailPresent === true,
111
+ b_tail_text: /·\s*1\s*hot/.test(b.tailText || ''),
112
+ b_tail_count_1: b.tailCount === '1',
113
+ b_has_fade_class: b.tailClassName.includes('anet-fade-in'),
114
+ c_tail_present: c.tailPresent === true,
115
+ c_tail_text: /·\s*2\s*hot/.test(c.tailText || ''),
116
+ c_tail_count_2: c.tailCount === '2',
117
+ c_has_fade_class: c.tailClassName.includes('anet-fade-in'),
118
+ };
119
+ const ok = Object.values(results).every(Boolean);
120
+ console.log(`${ok ? '✅' : '❌'} panel hot-tail fade:`, JSON.stringify(results),
121
+ `\n A (0 hot):`, a,
122
+ `\n B (1 hot):`, b,
123
+ `\n C (2 hot):`, c);
124
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,118 @@
1
+ /* Round 135 verification: both overlay panels (recent-signal +
2
+ * legend) drop-shadow boosts on mouseenter, restores on leave.
3
+ * Mirrors R18's KPI-card hover-elevation from the Overview page.
4
+ *
5
+ * Why both panels: each panel chrome already hosts interactive
6
+ * rows (R56/R116 recent rows, R55/R61 legend rows) and footer
7
+ * nav (R133). Hover-lift signals "this whole panel is alive"
8
+ * without misleading users that the panel itself is clickable —
9
+ * the underlying click affordances are still the rows + footer.
10
+ *
11
+ * State transitions tested:
12
+ * 1. Neither panel hovered → both data-topo-panel-hovered="false",
13
+ * both rect filters = baseline (2px/6px blur)
14
+ * 2. Hover recent → that panel hovered=true, filter boosted
15
+ * (4px/12px blur); legend still baseline
16
+ * 3. Leave recent (move far away) → recent restored
17
+ * 4. Hover legend → legend hovered=true, filter boosted
18
+ * 5. Leave legend → legend restored
19
+ *
20
+ * Filter inline-string detection: the boosted variant carries the
21
+ * `4px 12px` magic numbers in its drop-shadow argument.
22
+ */
23
+ import { chromium } from 'playwright';
24
+ import { readFileSync } from 'node:fs';
25
+
26
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
27
+ const browser = await chromium.launch({ headless: true });
28
+
29
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
30
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
31
+ await ctx.addInitScript(() => {
32
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
33
+ });
34
+
35
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
36
+ await ctx.route('**/api/hub/status*', async (route) => {
37
+ const r = await route.fetch();
38
+ const b = await r.json();
39
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
40
+ const mk = (alias, status) => ({
41
+ alias, status, 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', 'working'), mk('beta', 'idle'), mk('gamma', 'working'),
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
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
54
+ await page.waitForSelector('[data-topo-panel="recent"]', { timeout: 10000 });
55
+ await page.waitForTimeout(400);
56
+
57
+ const inspect = () => page.evaluate(() => {
58
+ const get = (key) => {
59
+ const g = document.querySelector(`[data-topo-panel="${key}"]`);
60
+ const rect = g?.querySelector('rect[data-topo-panel-elevation]');
61
+ return {
62
+ hovered: g?.getAttribute('data-topo-panel-hovered'),
63
+ filter: (rect?.getAttribute('style') || ''),
64
+ };
65
+ };
66
+ return { recent: get('recent'), legend: get('legend') };
67
+ });
68
+
69
+ // State 1 — neither hovered
70
+ const s1 = await inspect();
71
+
72
+ // State 2 — hover recent
73
+ await page.locator('[data-topo-panel="recent"]').hover();
74
+ await page.waitForTimeout(300);
75
+ const s2 = await inspect();
76
+
77
+ // State 3 — leave recent (move far away from both panels)
78
+ await page.mouse.move(700, 350);
79
+ await page.waitForTimeout(300);
80
+ const s3 = await inspect();
81
+
82
+ // State 4 — hover legend
83
+ await page.locator('[data-topo-panel="legend"]').hover();
84
+ await page.waitForTimeout(300);
85
+ const s4 = await inspect();
86
+
87
+ // State 5 — leave legend
88
+ await page.mouse.move(500, 350);
89
+ await page.waitForTimeout(300);
90
+ const s5 = await inspect();
91
+
92
+ await browser.close();
93
+
94
+ const isBoosted = (style) => /4px\s+12px/.test(style);
95
+ const isBase = (style) => /2px\s+6px/.test(style) && !/4px\s+12px/.test(style);
96
+
97
+ const results = {
98
+ s1_recent_notHovered: s1.recent.hovered === 'false',
99
+ s1_legend_notHovered: s1.legend.hovered === 'false',
100
+ s1_recent_baseShadow: isBase(s1.recent.filter),
101
+ s1_legend_baseShadow: isBase(s1.legend.filter),
102
+
103
+ s2_recent_hovered: s2.recent.hovered === 'true',
104
+ s2_recent_boosted: isBoosted(s2.recent.filter),
105
+ s2_legend_stillBase: s2.legend.hovered === 'false' && isBase(s2.legend.filter),
106
+
107
+ s3_recent_restored: s3.recent.hovered === 'false' && isBase(s3.recent.filter),
108
+
109
+ s4_legend_hovered: s4.legend.hovered === 'true',
110
+ s4_legend_boosted: isBoosted(s4.legend.filter),
111
+ s4_recent_stillBase: s4.recent.hovered === 'false' && isBase(s4.recent.filter),
112
+
113
+ s5_legend_restored: s5.legend.hovered === 'false' && isBase(s5.legend.filter),
114
+ };
115
+ const ok = Object.values(results).every(Boolean);
116
+ console.log(`${ok ? '✅' : '❌'} panel hover elevation:`, JSON.stringify(results),
117
+ `\n s1=`, s1, `\n s2=`, s2, `\n s3=`, s3, `\n s4=`, s4, `\n s5=`, s5);
118
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,82 @@
1
+ /* Round 103 verification: flow-particle `<animateMotion begin>` is
2
+ * unique per edge, staggered by `-(index × 0.37) % duration s`. Pre-
3
+ * R103 all edges' particles fired at t=0 in lockstep; the stagger
4
+ * gives the canvas an organic "many independent rivers" feel.
5
+ *
6
+ * Verifies:
7
+ * - every edge has a `begin` attribute on its <animateMotion>
8
+ * - all begin values are distinct (no two edges share a phase)
9
+ * - the format is "-Ns" (negative offset for retroactive cycle start)
10
+ * - reducedMotion still suppresses the particle entirely
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+ const fresh = new Date(Date.now() - 30 * 1000).toISOString();
18
+
19
+ const probe = async (reducedMotion) => {
20
+ const ctx = await browser.newContext({
21
+ viewport: { width: 1400, height: 1500 },
22
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
23
+ });
24
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
25
+ await ctx.addInitScript(() => {
26
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
27
+ });
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) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta'), mk('epsilon')] } });
38
+ });
39
+ const now = Date.now();
40
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
41
+ id, from_alias, to_alias, content: 'hi', network_id: 'default',
42
+ created_at: new Date(now - ageMs).toISOString(),
43
+ });
44
+ // Five distinct edges → five animations.
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
46
+ mkMsg('m1', 'alpha', 'beta', 30 * 1000),
47
+ mkMsg('m2', 'gamma', 'delta', 35 * 1000),
48
+ mkMsg('m3', 'beta', 'gamma', 40 * 1000),
49
+ mkMsg('m4', 'delta', 'epsilon', 45 * 1000),
50
+ mkMsg('m5', 'epsilon', 'alpha', 50 * 1000),
51
+ ] } }));
52
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
56
+ await page.waitForTimeout(400);
57
+
58
+ const beginValues = await page.evaluate(() => {
59
+ return [...document.querySelectorAll('animateMotion')]
60
+ .map(a => a.getAttribute('begin'));
61
+ });
62
+ await ctx.close();
63
+ return beginValues;
64
+ };
65
+
66
+ const normal = await probe(false);
67
+ const reduced = await probe(true);
68
+ await browser.close();
69
+
70
+ const results = {
71
+ normal_hasAtLeast5: normal.length >= 5,
72
+ normal_allHaveBegin: normal.every(b => typeof b === 'string' && b.length > 0),
73
+ normal_allNegativeOffset: normal.every(b => /^-\d/.test(b || '')),
74
+ normal_allDistinct: new Set(normal).size === normal.length,
75
+ normal_firstIsZero: normal[0] === '-0.000s',
76
+ reduced_noParticles: reduced.length === 0,
77
+ };
78
+ const ok = Object.values(results).every(Boolean);
79
+ console.log(`${ok ? '✅' : '❌'} particle stagger:`, JSON.stringify(results),
80
+ `\n normal=`, normal,
81
+ `\n reduced=`, reduced);
82
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,129 @@
1
+ /* Round 125 verification: pin-intersection chip warning tint when
2
+ * the intersection is zero.
3
+ *
4
+ * R124 added the "match: N pins · K" chip after the last filter
5
+ * pill, gated to ≥ 2 active pin dims. It stayed neutral gray
6
+ * regardless of whether K was 12 or 0 — but K=0 means "your pins
7
+ * cancel out" and the canvas dims to 0.28 everywhere with no
8
+ * positive signal. The neutral "· 0" tail reads as just another
9
+ * number, indistinguishable from "· 12".
10
+ *
11
+ * R125 flips the chip to amber + adds a ⚠ glyph when K=0. Tooltip
12
+ * grows the "release one to widen" hint. Color choice: amber
13
+ * (#d97706 light / #fbbf24 dark) — distinct from every pill color
14
+ * AND from the gray non-empty intersection state.
15
+ *
16
+ * Fleet:
17
+ * alpha (working, vendor=A)
18
+ * beta (working, vendor=O)
19
+ * gamma (idle, vendor=A)
20
+ * delta (idle, vendor=O)
21
+ *
22
+ * Path:
23
+ * 1. Pin status=working → chip hidden (1 pin)
24
+ * 2. + Pin vendor=A → intersection {alpha} = 1 → chip gray, no ⚠
25
+ * 3. Esc, then pin status=offline + vendor=A → intersection 0
26
+ * (alpha+gamma have vendor=A but neither is offline)
27
+ * → chip amber + ⚠ glyph + tooltip mentions "release one to widen"
28
+ */
29
+ import { chromium } from 'playwright';
30
+ import { readFileSync } from 'node:fs';
31
+
32
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
33
+ const browser = await chromium.launch({ headless: true });
34
+
35
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1100 } });
36
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
37
+ await ctx.addInitScript(() => {
38
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
39
+ });
40
+
41
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
42
+ await ctx.route('**/api/hub/status*', async (route) => {
43
+ const r = await route.fetch();
44
+ const b = await r.json();
45
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
46
+ const mk = (alias, status, model) => ({
47
+ alias, status, model, runtime: 'cli-claude-code',
48
+ network_id: nid, project_dir: null,
49
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
50
+ });
51
+ await route.fulfill({ response: r, json: { ...b, sessions: [
52
+ mk('alpha', 'working', 'claude-opus-4'),
53
+ mk('beta', 'working', 'gpt-4o'),
54
+ mk('gamma', 'idle', 'claude-sonnet-4'),
55
+ mk('delta', 'idle', 'gpt-4o-mini'),
56
+ ] } });
57
+ });
58
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
59
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
60
+
61
+ const page = await ctx.newPage();
62
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
63
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
64
+ await page.waitForTimeout(400);
65
+
66
+ const readChip = () => page.evaluate(() => {
67
+ const el = document.querySelector('[data-pin-intersection]');
68
+ if (!el) return null;
69
+ // background color comes from inline style; parse for amber substring.
70
+ const inline = el.getAttribute('style') || '';
71
+ return {
72
+ dimCount: parseInt(el.getAttribute('data-pin-dim-count') || '0', 10),
73
+ matchCount: parseInt(el.getAttribute('data-pin-intersection-count') || '0', 10),
74
+ empty: el.getAttribute('data-pin-intersection-empty'),
75
+ text: el.textContent?.trim(),
76
+ title: el.getAttribute('title'),
77
+ // Browser normalises inline hex → rgb() when reading via
78
+ // getAttribute('style'). Cyber theme: #fbbf24 → rgb(251, 191, 36);
79
+ // gray #94a3b8 → rgb(148, 163, 184). Light theme would emit
80
+ // rgb(217, 119, 6) and rgb(71, 85, 105) — accept both.
81
+ // Inline background uses rgba(...) because of the alpha suffix
82
+ // we pass; foreground color is rgb(...). Match both with rgba?.
83
+ styleHasAmber: /rgba?\(251,\s*191,\s*36/i.test(inline) || /rgba?\(217,\s*119,\s*6/i.test(inline),
84
+ styleHasGray: /rgba?\(148,\s*163,\s*184/i.test(inline) || /rgba?\(71,\s*85,\s*105/i.test(inline),
85
+ };
86
+ });
87
+
88
+ // All four sessions are online in this fleet, so the offline
89
+ // pressure-seg doesn't render. Use the R69 CustomEvent contract
90
+ // to drive pins — same handler at line 920+ of TopoGraph.tsx.
91
+ const pin = (detail) => page.evaluate(d => {
92
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: d }));
93
+ }, detail);
94
+
95
+ // State 1 — only status pinned
96
+ await pin({ kind: 'status', value: 'working' });
97
+ await page.waitForTimeout(200);
98
+ const s1 = await readChip();
99
+
100
+ // State 2 — + vendor=A (working ∩ A = {alpha}) → 1 match, gray
101
+ await pin({ kind: 'vendor', value: 'A' });
102
+ await page.waitForTimeout(200);
103
+ const s2 = await readChip();
104
+
105
+ // Esc, then pin status=offline + vendor=A → 0 matches (alpha/gamma are
106
+ // A but online; beta/delta are O). Empty intersection → amber + ⚠.
107
+ await pin({ kind: 'clear' });
108
+ await page.waitForTimeout(200);
109
+ await pin({ kind: 'status', value: 'offline' });
110
+ await pin({ kind: 'vendor', value: 'A' });
111
+ await page.waitForTimeout(200);
112
+ const s3 = await readChip();
113
+
114
+ await browser.close();
115
+
116
+ const results = {
117
+ s1_onePin_chipHidden: s1 === null,
118
+ s2_twoPins_chipShown: !!s2 && s2.dimCount === 2 && s2.matchCount === 1,
119
+ s2_nonEmpty_grayBg: !!s2 && s2.empty === 'false' && s2.styleHasGray && !s2.styleHasAmber,
120
+ s2_noWarnGlyph: !!s2 && !s2.text?.includes('⚠'),
121
+ s3_twoPins_zeroMatch: !!s3 && s3.dimCount === 2 && s3.matchCount === 0,
122
+ s3_empty_amberBg: !!s3 && s3.empty === 'true' && s3.styleHasAmber && !s3.styleHasGray,
123
+ s3_text_hasWarnGlyph: !!s3 && (s3.text || '').includes('⚠'),
124
+ s3_tooltip_widenHint: !!s3 && (s3.title || '').includes('release one to widen'),
125
+ };
126
+ const ok = Object.values(results).every(Boolean);
127
+ console.log(`${ok ? '✅' : '❌'} pin-intersection empty:`, JSON.stringify(results),
128
+ `\n s1=`, s1, `\n s2=`, s2, `\n s3=`, s3);
129
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,121 @@
1
+ /* Round 124 verification: pin-intersection summary chip.
2
+ *
3
+ * The four filter pills (R64 status / R63 group / R89 vendor /
4
+ * R119 edge) each show their own dim's match count in isolation.
5
+ * But the node-opacity chain (lines 2723-2741 of TopoGraph.tsx)
6
+ * AND-composes all four pin dimensions — every miss dims the node.
7
+ * So two active pins produce an intersection ≤ either individual
8
+ * count, and the existing pills don't surface that.
9
+ *
10
+ * R124 adds a small "match: N pins · K" chip after the last pill
11
+ * when ≥ 2 pin dims are active. K = nodes that satisfy ALL active
12
+ * pins. Single pin keeps its old pill story (chip hidden).
13
+ *
14
+ * Fleet:
15
+ * alpha (working, vendor=A, group=agents)
16
+ * beta (working, vendor=O, group=agents)
17
+ * gamma (idle, vendor=A, group=agents)
18
+ * delta (working, vendor=A, group=infra)
19
+ *
20
+ * Test path:
21
+ * 1. No pin → chip hidden
22
+ * 2. Pin status=working → chip hidden (1 pin)
23
+ * 3. + Pin vendor=A → chip "2 pins · 2"
24
+ * (working AND vendor=A: alpha, delta)
25
+ * 4. + Pin group=agents → chip "3 pins · 1"
26
+ * (working AND A AND agents: alpha only)
27
+ * 5. Esc → chip hidden
28
+ */
29
+ import { chromium } from 'playwright';
30
+ import { readFileSync } from 'node:fs';
31
+
32
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
33
+ const browser = await chromium.launch({ headless: true });
34
+
35
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1100 } });
36
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
37
+ await ctx.addInitScript(() => {
38
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
39
+ });
40
+
41
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
42
+ await ctx.route('**/api/hub/status*', async (route) => {
43
+ const r = await route.fetch();
44
+ const b = await r.json();
45
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
46
+ const mk = (alias, status, model) => ({
47
+ alias, status, model, runtime: 'cli-claude-code',
48
+ network_id: nid, project_dir: alias.startsWith('infra-') ? '/srv/infra' : '/srv/agents',
49
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
50
+ });
51
+ await route.fulfill({ response: r, json: { ...b, sessions: [
52
+ mk('agents-alpha', 'working', 'claude-opus-4'),
53
+ mk('agents-beta', 'working', 'gpt-4o'),
54
+ mk('agents-gamma', 'idle', 'claude-sonnet-4'),
55
+ mk('infra-delta', 'working', 'claude-haiku-4'),
56
+ ] } });
57
+ });
58
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
59
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
60
+
61
+ const page = await ctx.newPage();
62
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
63
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
64
+ await page.waitForTimeout(400);
65
+
66
+ const readChip = () => page.evaluate(() => {
67
+ const el = document.querySelector('[data-pin-intersection]');
68
+ if (!el) return null;
69
+ return {
70
+ dimCount: parseInt(el.getAttribute('data-pin-dim-count') || '0', 10),
71
+ matchCount: parseInt(el.getAttribute('data-pin-intersection-count') || '0', 10),
72
+ aliases: (el.getAttribute('data-pin-intersection-aliases') || '').split(',').filter(Boolean),
73
+ text: el.textContent?.trim(),
74
+ };
75
+ });
76
+
77
+ // State 1 — no pins
78
+ const s1 = await readChip();
79
+
80
+ // State 2 — pin status=working via pressure-bar segment
81
+ await page.locator('[data-pressure-seg="working"]').click();
82
+ await page.waitForTimeout(200);
83
+ const s2 = await readChip();
84
+
85
+ // State 3 — + pin vendor=A
86
+ await page.locator('[data-vendor-letter="A"]').click();
87
+ await page.waitForTimeout(200);
88
+ const s3 = await readChip();
89
+
90
+ // State 4 — + pin group=agents via the R69 CustomEvent contract.
91
+ // Direct dispatch is mode-independent (group-label hit-rects only
92
+ // render in grid layout; default is ring). The handler at line 945
93
+ // of TopoGraph.tsx accepts { kind: 'group', value: <key> }.
94
+ await page.evaluate(() => {
95
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: 'agents' } }));
96
+ });
97
+ await page.waitForTimeout(200);
98
+ const s4 = await readChip();
99
+
100
+ // State 5 — Esc clears all pins
101
+ await page.keyboard.press('Escape');
102
+ await page.waitForTimeout(200);
103
+ const s5 = await readChip();
104
+
105
+ await browser.close();
106
+
107
+ const results = {
108
+ s1_noPins_chipHidden: s1 === null,
109
+ s2_onePin_chipHidden: s2 === null,
110
+ s3_twoPins_chipShown: !!s3 && s3.dimCount === 2,
111
+ s3_intersection_2: !!s3 && s3.matchCount === 2,
112
+ s3_aliases_alphaDelta: !!s3 && s3.aliases.includes('agents-alpha') && s3.aliases.includes('infra-delta'),
113
+ s4_threePins_chipShown: !!s4 && s4.dimCount === 3,
114
+ s4_intersection_1: !!s4 && s4.matchCount === 1,
115
+ s4_aliases_alphaOnly: !!s4 && s4.aliases.length === 1 && s4.aliases[0] === 'agents-alpha',
116
+ s5_escClears_chipHidden: s5 === null,
117
+ };
118
+ const ok = Object.values(results).every(Boolean);
119
+ console.log(`${ok ? '✅' : '❌'} pin-intersection chip:`, JSON.stringify(results),
120
+ `\n s1=`, s1, `\n s2=`, s2, `\n s3=`, s3, `\n s4=`, s4, `\n s5=`, s5);
121
+ process.exit(ok ? 0 : 1);