@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,99 @@
1
+ /* Round 107 verification: group-label hit `<rect>` picks up a tint
2
+ * on hover and pin — extends the R104 (recent-signal) / R105
3
+ * (legend) list-item-tint pattern to the SVG group labels. The tint
4
+ * is pal.legendAccent (cyan) since groups don't carry an inherent
5
+ * swatch; same colour family R68 already uses for hovered/pinned
6
+ * group-box strokes.
7
+ *
8
+ * Verifies idle / hover / pinned via the new data-group-label-tinted
9
+ * attribute + fill/opacity. Pin opacity stronger than hover.
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const browser = await chromium.launch({ headless: true });
16
+
17
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try {
21
+ localStorage.setItem('anet-theme', 'cyber');
22
+ localStorage.setItem('anet-topo-layout', 'grid');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ sessionStorage.removeItem('anet-topo-pinned-group');
25
+ } catch {}
26
+ });
27
+
28
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
29
+ await ctx.route('**/api/hub/status*', async (route) => {
30
+ const r = await route.fetch();
31
+ const b = await r.json();
32
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
33
+ const mk = (alias) => ({
34
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
35
+ network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ });
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [
39
+ mk('alpha1'), mk('alpha2'), mk('alpha3'),
40
+ mk('beta1'), mk('beta2'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
47
+ await page.waitForFunction(() => document.querySelectorAll('[data-group-label-hit]').length >= 2, { timeout: 30000 });
48
+ await page.waitForTimeout(400);
49
+
50
+ const readLabel = (key) => page.evaluate((k) => {
51
+ const g = document.querySelector(`[data-group-label-hit="${k}"]`);
52
+ if (!g) return null;
53
+ const rect = g.querySelector('rect');
54
+ return {
55
+ tinted: rect?.getAttribute('data-group-label-tinted') || '',
56
+ fill: rect?.getAttribute('fill') || '',
57
+ opacity: rect?.getAttribute('opacity') || '',
58
+ };
59
+ }, key);
60
+
61
+ const beforeAlpha = await readLabel('alpha');
62
+ const beforeBeta = await readLabel('beta');
63
+
64
+ // Hover alpha label.
65
+ await page.locator('[data-group-label-hit="alpha"]').hover();
66
+ await page.waitForTimeout(250);
67
+ const onHoverAlpha = await readLabel('alpha');
68
+ const onHoverBeta = await readLabel('beta');
69
+
70
+ // Move pointer far away.
71
+ await page.mouse.move(10, 10);
72
+ await page.waitForTimeout(250);
73
+
74
+ // Pin beta.
75
+ await page.locator('[data-group-label-hit="beta"]').click();
76
+ await page.mouse.move(10, 10);
77
+ await page.waitForTimeout(250);
78
+ const pinBeta = await readLabel('beta');
79
+ const pinAlphaUnchanged = await readLabel('alpha');
80
+
81
+ await browser.close();
82
+
83
+ const opacity = (s) => parseFloat(s) || 0;
84
+ const results = {
85
+ before_alphaTransparent: beforeAlpha?.tinted === 'none' && beforeAlpha?.fill === 'transparent',
86
+ before_betaTransparent: beforeBeta?.tinted === 'none' && beforeBeta?.fill === 'transparent',
87
+ hover_alphaTinted: onHoverAlpha?.tinted === 'hover' && onHoverAlpha?.fill !== 'transparent',
88
+ hover_alphaOpacityLow: opacity(onHoverAlpha?.opacity || '') > 0 && opacity(onHoverAlpha?.opacity || '') < 0.2,
89
+ hover_betaUnchanged: onHoverBeta?.tinted === 'none',
90
+ pin_betaPinned: pinBeta?.tinted === 'pinned' && pinBeta?.fill !== 'transparent',
91
+ pin_strongerThanHover: opacity(pinBeta?.opacity || '') > opacity(onHoverAlpha?.opacity || ''),
92
+ pin_alphaClean: pinAlphaUnchanged?.tinted === 'none',
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} group-label tint:`, JSON.stringify(results),
96
+ `\n beforeAlpha=`, beforeAlpha,
97
+ `\n onHoverAlpha=`, onHoverAlpha,
98
+ `\n pinBeta=`, pinBeta);
99
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,83 @@
1
+ /* Round 99 verification: group label <title> tooltip lists members
2
+ * + status breakdown. Same R97/R98 pattern — anywhere the UI shows
3
+ * "alpha · 3" should hover-explain WHICH 3.
4
+ *
5
+ * Fleet: alpha group has 3 members (2 working + 1 idle), beta has
6
+ * 2 idle. Pin alpha to confirm the title's last line flips from
7
+ * "click to pin" → "click to release pin".
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const browser = await chromium.launch({ headless: true });
14
+
15
+ const ctx = await browser.newContext({ viewport: { width: 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', 'grid');
21
+ sessionStorage.setItem('anet_v3_auth', '1');
22
+ sessionStorage.removeItem('anet-topo-pinned-group');
23
+ } catch {}
24
+ });
25
+
26
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
27
+ await ctx.route('**/api/hub/status*', async (route) => {
28
+ const r = await route.fetch();
29
+ const b = await r.json();
30
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
31
+ const mk = (alias, status) => ({
32
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
33
+ network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ });
36
+ await route.fulfill({ response: r, json: { ...b, sessions: [
37
+ mk('alpha1', 'working'),
38
+ mk('alpha2', 'working'),
39
+ mk('alpha3', 'idle'),
40
+ mk('beta1', 'idle'),
41
+ mk('beta2', 'idle'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
48
+ await page.waitForFunction(() => document.querySelectorAll('[data-group-label-hit]').length >= 2, { timeout: 30000 });
49
+ await page.waitForTimeout(400);
50
+
51
+ const readTitle = (groupKey) => page.evaluate((k) => {
52
+ const g = document.querySelector(`[data-group-label-hit="${k}"]`);
53
+ return g?.querySelector('title')?.textContent || '';
54
+ }, groupKey);
55
+
56
+ const alphaTitle = await readTitle('alpha');
57
+ const betaTitle = await readTitle('beta');
58
+
59
+ // Pin alpha — last line should flip.
60
+ await page.locator('[data-group-label-hit="alpha"]').click();
61
+ await page.waitForTimeout(250);
62
+ const alphaTitlePinned = await readTitle('alpha');
63
+
64
+ await browser.close();
65
+
66
+ const results = {
67
+ alpha_heading: /^alpha \(3 members\)/m.test(alphaTitle),
68
+ alpha_statusSummary: /2 working · 1 idle/.test(alphaTitle),
69
+ alpha_listsMembers: /alpha1.*alpha2.*alpha3/.test(alphaTitle),
70
+ alpha_pinHint: /click to pin this group/.test(alphaTitle),
71
+ beta_heading: /^beta \(2 members\)/m.test(betaTitle),
72
+ beta_statusSummary: /2 idle/.test(betaTitle),
73
+ beta_noWorkingInSummary: !/working/.test(betaTitle),
74
+ beta_listsMembers: /beta1.*beta2/.test(betaTitle),
75
+ pinned_hintFlipped: /click to release pin/.test(alphaTitlePinned),
76
+ pinned_keepsMembers: /alpha1.*alpha2.*alpha3/.test(alphaTitlePinned),
77
+ };
78
+ const ok = Object.values(results).every(Boolean);
79
+ console.log(`${ok ? '✅' : '❌'} group-label title:`, JSON.stringify(results),
80
+ `\n alpha:\n${alphaTitle}`,
81
+ `\n beta:\n${betaTitle}`,
82
+ `\n alphaPinned:\n${alphaTitlePinned}`);
83
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,119 @@
1
+ /* Round 63 verification: clicking the group label pins the group focus
2
+ * via pinnedGroup. activeGroup = hoveredGroup ?? pinnedGroup is read by
3
+ * the R8 inFocus dim mechanic and the R53 in-group edge boost.
4
+ *
5
+ * - Click a group label → that group's nodes stay bright, others dim
6
+ * via the existing inFocus formula (opacity 0.32).
7
+ * - Move mouse away → pin persists (transient hover cleared).
8
+ * - In-group edge between OTHER team members brightens 1.3× even
9
+ * without an alias hover.
10
+ * - Esc clears the pin (R62 extended).
11
+ * - aria-pressed flips on the group <text>.
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const browser = await chromium.launch({ headless: true });
18
+
19
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'grid');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const sessions = [
35
+ { alias: 'alpha1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
36
+ { alias: 'alpha2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
37
+ { alias: 'alpha3', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
38
+ { alias: 'beta', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ ];
40
+ await route.fulfill({ response: r, json: { ...b, sessions } });
41
+ });
42
+ const now = new Date().toISOString();
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
44
+ // alpha2 → alpha3: both in alpha group, neither is the pinned-group's "exact alias"
45
+ { from_alias: 'alpha2', to_alias: 'alpha3', content: 'm', created_at: now },
46
+ // alpha1 → beta: alpha1 in pinned group, beta outside → should dim
47
+ { from_alias: 'alpha1', to_alias: 'beta', content: 'm', created_at: now },
48
+ ] } }));
49
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
52
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
53
+ await page.waitForTimeout(600);
54
+
55
+ const nodeOpacities = () => page.evaluate(() => {
56
+ const o = {};
57
+ for (const a of ['alpha1', 'alpha2', 'alpha3', 'beta']) {
58
+ const g = document.querySelector(`g[data-node="${a}"]`);
59
+ o[a] = g ? +(g.style.opacity || '1') : null;
60
+ }
61
+ return o;
62
+ });
63
+ const edgeOpacities = () => page.evaluate(() => {
64
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
65
+ const out = {};
66
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
67
+ const t = g.querySelector('path[data-edge-hitbox] title');
68
+ if (!t) continue;
69
+ const route = (t.textContent || '').split('\n')[0];
70
+ const base = [...g.querySelectorAll(':scope > path')].find(
71
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
72
+ );
73
+ if (base) out[route] = +base.getAttribute('opacity');
74
+ }
75
+ return out;
76
+ });
77
+
78
+ const before = await nodeOpacities();
79
+ const beforeEdges = await edgeOpacities();
80
+
81
+ // Click the alpha group label via its R63 hitbox wrapper.
82
+ const alphaLabel = page.locator('g[data-group-label-hit="alpha"]').first();
83
+ const exists = await alphaLabel.count() > 0;
84
+ if (!exists) { console.log('❌ alpha group label hit not found'); process.exit(1); }
85
+ await alphaLabel.click({ force: true });
86
+ // Mouse away so we observe pin-only state.
87
+ await page.mouse.move(10, 10);
88
+ await page.waitForTimeout(300);
89
+ const afterPinNodes = await nodeOpacities();
90
+ const afterPinEdges = await edgeOpacities();
91
+ const ariaPressed = await page.evaluate(() => document.querySelector('g[data-group-label-hit="alpha"]')?.getAttribute('aria-pressed'));
92
+
93
+ // Esc to release.
94
+ await page.evaluate(() => document.activeElement && typeof document.activeElement.blur === 'function' && document.activeElement.blur());
95
+ await page.keyboard.press('Escape');
96
+ await page.waitForTimeout(300);
97
+ const afterEscNodes = await nodeOpacities();
98
+ const ariaAfterEsc = await page.evaluate(() => document.querySelector('g[data-group-label-hit="alpha"]')?.getAttribute('aria-pressed'));
99
+
100
+ await browser.close();
101
+
102
+ const bright = (v) => v != null && v >= 0.55;
103
+ const dim = (v) => v != null && v < 0.4;
104
+ const results = {
105
+ before_baseline: bright(before.alpha1) && bright(before.alpha2) && bright(before.alpha3) && bright(before.beta),
106
+ pin_keepsAlphaTeam: bright(afterPinNodes.alpha1) && bright(afterPinNodes.alpha2) && bright(afterPinNodes.alpha3),
107
+ pin_dimsBeta: dim(afterPinNodes.beta),
108
+ pin_ariaPressed: ariaPressed === 'true',
109
+ pin_inGroupEdgeBoosts: afterPinEdges['alpha2 → alpha3'] > beforeEdges['alpha2 → alpha3'] * 1.15,
110
+ pin_leavingEdgeDims: afterPinEdges['alpha1 → beta'] < beforeEdges['alpha1 → beta'] * 0.6,
111
+ esc_releasesPin: bright(afterEscNodes.alpha1) && bright(afterEscNodes.beta),
112
+ esc_ariaBackToFalse: ariaAfterEsc === 'false',
113
+ };
114
+ const ok = Object.values(results).every(Boolean);
115
+ console.log(`${ok ? '✅' : '❌'} group pin:`, JSON.stringify(results),
116
+ `\n before nodes=`, before, ` edges=`, beforeEdges,
117
+ `\n afterPin nodes=`, afterPinNodes, ` edges=`, afterPinEdges, ` aria=${ariaPressed}`,
118
+ `\n afterEsc nodes=`, afterEscNodes, ` aria=${ariaAfterEsc}`);
119
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 58 verification: group label includes a status-mix pip strip
2
+ * showing how many members are working / idle / offline.
3
+ *
4
+ * Sessions (mixed-status alpha team + singleton beta):
5
+ * alpha1 — working
6
+ * alpha2 — working
7
+ * alpha3 — idle
8
+ * alpha4 — offline
9
+ * beta — idle (singleton, no group box)
10
+ *
11
+ * Grid layout draws a group box for alpha (4 members). Label should
12
+ * end in something like "alpha · 4 · 2w 1i 1o". Status pips render
13
+ * only when their count > 0, so a uniform group reads more simply.
14
+ */
15
+ import { chromium } from 'playwright';
16
+ import { readFileSync } from 'node:fs';
17
+
18
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
19
+ const browser = await chromium.launch({ headless: true });
20
+
21
+ async function probe(sessions) {
22
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try {
26
+ localStorage.setItem('anet-theme', 'cyber');
27
+ localStorage.setItem('anet-topo-layout', 'grid');
28
+ sessionStorage.setItem('anet_v3_auth', '1');
29
+ } catch {}
30
+ });
31
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
32
+ const stale = new Date(Date.now() - 30 * 60 * 1000).toISOString();
33
+ await ctx.route('**/api/hub/status*', async (route) => {
34
+ const r = await route.fetch();
35
+ const b = await r.json();
36
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
37
+ const seeded = sessions.map(s => ({
38
+ alias: s.alias, status: s.status,
39
+ network_id: nid, project_dir: null,
40
+ created_at: s.status === 'offline' ? stale : fresh,
41
+ updated_at: s.status === 'offline' ? stale : fresh,
42
+ last_seen_at: s.status === 'offline' ? stale : fresh,
43
+ }));
44
+ await route.fulfill({ response: r, json: { ...b, sessions: seeded } });
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
+ const page = await ctx.newPage();
49
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
50
+ await page.waitForFunction(c => document.querySelectorAll('g[data-node]').length === c, sessions.length, { timeout: 30000 });
51
+ await page.waitForTimeout(500);
52
+ const labels = await page.evaluate(() => {
53
+ const out = {};
54
+ for (const t of document.querySelectorAll('text[data-group-label]')) {
55
+ out[t.getAttribute('data-group-label')] = (t.textContent || '').trim();
56
+ }
57
+ return out;
58
+ });
59
+ await ctx.close();
60
+ return labels;
61
+ }
62
+
63
+ // Grouping is consecutive-run on the sorted [...online, ...offline] array,
64
+ // so an offline member of the "alpha" prefix family ends up in a separate
65
+ // band (it falls into the offline tier rather than joining the online
66
+ // alpha group). All members that share a multi-member band are by
67
+ // construction online, so the "offline pip" case is unreachable via the
68
+ // group label and the test covers only the working/idle dimensions here.
69
+ const mixed = await probe([
70
+ { alias: 'alpha1', status: 'working' },
71
+ { alias: 'alpha2', status: 'working' },
72
+ { alias: 'alpha3', status: 'idle' },
73
+ { alias: 'beta', status: 'idle' },
74
+ ]);
75
+
76
+ const allWorking = await probe([
77
+ { alias: 'gamma1', status: 'working' },
78
+ { alias: 'gamma2', status: 'working' },
79
+ { alias: 'gamma3', status: 'working' },
80
+ { alias: 'beta', status: 'idle' },
81
+ ]);
82
+
83
+ await browser.close();
84
+
85
+ const alphaLabel = mixed.alpha || '';
86
+ const gammaLabel = allWorking.gamma || '';
87
+
88
+ const results = {
89
+ alpha_keyPresent: /^alpha/.test(alphaLabel),
90
+ alpha_countCorrect: /·\s*3/.test(alphaLabel),
91
+ alpha_workingPip: /2w/.test(alphaLabel),
92
+ alpha_idlePip: /1i/.test(alphaLabel),
93
+ alpha_noOfflinePip: !/\d+o\b/.test(alphaLabel),
94
+ // Uniform-status group: only the working pip renders.
95
+ gamma_workingPip: /3w/.test(gammaLabel),
96
+ gamma_noIdlePip: !/\d+i\b/.test(gammaLabel),
97
+ gamma_noOfflinePip: !/\d+o\b/.test(gammaLabel),
98
+ };
99
+ const ok = Object.values(results).every(Boolean);
100
+ console.log(`${ok ? '✅' : '❌'} group status pips:`, JSON.stringify(results),
101
+ `\n alpha="${alphaLabel}"`,
102
+ `\n gamma="${gammaLabel}"`);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,95 @@
1
+ /* Round 173 verification: group boxes (grid layout) fade-in
2
+ * alongside the R9/R172 first-paint wave.
3
+ *
4
+ * Pre-R173 the structural box frames appeared instantly while
5
+ * nodes inside eased in via R9 (0-540ms) and edges via R172
6
+ * (280-980ms). The reveal felt like 'frame slams down, nodes
7
+ * drift in'.
8
+ *
9
+ * R173 wraps each group box <g> in:
10
+ * className="transition-opacity anet-fade-in"
11
+ * style={{ animationDelay: 'boxIdx × 60ms (cap 8)' }}
12
+ * data-group-fade-delay={boxIdx × 60}
13
+ *
14
+ * Test:
15
+ * 1. Force grid layout
16
+ * 2. Mock 4 prefix-clustered sessions → 2 groups
17
+ * 3. Probe each [data-group] wrapper
18
+ * 4. Assert anet-fade-in className present
19
+ * 5. Assert animation-delay 0ms, 60ms
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const browser = await chromium.launch({ headless: true });
26
+
27
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
28
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
29
+ await ctx.addInitScript(() => {
30
+ try {
31
+ localStorage.setItem('anet-theme', 'cyber');
32
+ sessionStorage.setItem('anet_v3_auth', '1');
33
+ localStorage.setItem('anet-topo-layout', 'grid');
34
+ } catch {}
35
+ });
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: 'cli-claude-code',
44
+ network_id: nid, project_dir: null,
45
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
46
+ });
47
+ // 2 distinct prefix groups, 2 sessions each → 2 group boxes
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [
49
+ mk('agents-a1'), mk('agents-a2'),
50
+ mk('infra-b1'), mk('infra-b2'),
51
+ ] } });
52
+ });
53
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
54
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
55
+
56
+ const page = await ctx.newPage();
57
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
58
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
59
+ await page.waitForSelector('[data-group]', { timeout: 10000 });
60
+ await page.waitForTimeout(400);
61
+
62
+ const probe = await page.evaluate(() => {
63
+ const boxes = [...document.querySelectorAll('g[data-group]')];
64
+ return boxes.map(b => ({
65
+ key: b.getAttribute('data-group'),
66
+ hasFadeClass: (b.getAttribute('class') || '').includes('anet-fade-in'),
67
+ fadeDelayAttr: parseFloat(b.getAttribute('data-group-fade-delay') || ''),
68
+ animationDelay: b.style.animationDelay || getComputedStyle(b).animationDelay,
69
+ }));
70
+ });
71
+
72
+ await browser.close();
73
+
74
+ const parseDelay = (s) => {
75
+ if (!s) return null;
76
+ if (s.endsWith('ms')) return parseFloat(s);
77
+ if (s.endsWith('s')) return parseFloat(s) * 1000;
78
+ return parseFloat(s);
79
+ };
80
+ const delays = probe.map(p => parseDelay(p.animationDelay));
81
+
82
+ const results = {
83
+ two_boxes_found: probe.length === 2,
84
+ all_have_fade_class: probe.every(p => p.hasFadeClass),
85
+ box0_delay_attr_0: probe[0]?.fadeDelayAttr === 0,
86
+ box1_delay_attr_60: probe[1]?.fadeDelayAttr === 60,
87
+ box0_anim_delay_0ms: delays[0] === 0,
88
+ box1_anim_delay_60ms: Math.abs(delays[1] - 60) < 0.5,
89
+ delays_strictly_increase: delays[0] < delays[1],
90
+ };
91
+ const ok = Object.values(results).every(Boolean);
92
+ console.log(`${ok ? '✅' : '❌'} groupbox fade-in:`, JSON.stringify(results),
93
+ `\n boxes =`, probe,
94
+ `\n delays (ms) =`, delays);
95
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,127 @@
1
+ /* Round 142 verification: group boxes (R85+R68) gain a drop-
2
+ * shadow lift on hover or pin, mirroring R135's overlay panel
3
+ * hover-elevation. Closes the "interactive surface elevates"
4
+ * vocabulary across canvas-side major surfaces:
5
+ * R18 KPI cards (Overview)
6
+ * R135 overlay panels (recent-signal + legend)
7
+ * R142 group boxes (canvas-level filter)
8
+ *
9
+ * Each carries a soft outward shadow when hovered/selected.
10
+ *
11
+ * States walked (grid layout, three prefix groups):
12
+ * 1. Idle → all 3 boxes: data-group-box-lifted="false", no filter
13
+ * 2. Hover infra label → infra lifted=true, filter set; others stay flat
14
+ * 3. Leave hover → infra restored to flat
15
+ * 4. Pin agents via CustomEvent → agents lifted=true (R63 pin path)
16
+ * 5. Esc → agents back to flat
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: 1600, 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 {
28
+ localStorage.setItem('anet-theme', 'cyber');
29
+ sessionStorage.setItem('anet_v3_auth', '1');
30
+ // R85 group boxes only render in grid layout
31
+ localStorage.setItem('anet-topo-layout', 'grid');
32
+ } 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('agents-a1', 'working'), mk('agents-a2', 'idle'), mk('agents-a3', 'idle'),
47
+ mk('infra-b1', 'working'), mk('infra-b2', 'idle'),
48
+ mk('build-c1', 'idle'), mk('build-c2', 'idle'),
49
+ ] } });
50
+ });
51
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
52
+ await ctx.route('**/api/hub/tasks*', (route) => route.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 === 7, { timeout: 30000 });
57
+ await page.waitForSelector('[data-group]', { timeout: 10000 });
58
+ await page.waitForTimeout(500);
59
+
60
+ const inspectAll = () => page.evaluate(() => {
61
+ const boxes = [...document.querySelectorAll('rect[data-group-box-pinned]')];
62
+ return boxes.map(rect => {
63
+ const parentG = rect.closest('g[data-group]');
64
+ return {
65
+ group: parentG?.getAttribute('data-group'),
66
+ lifted: rect.getAttribute('data-group-box-lifted'),
67
+ filter: rect.getAttribute('filter'),
68
+ pinned: rect.getAttribute('data-group-box-pinned'),
69
+ };
70
+ });
71
+ });
72
+
73
+ const state1 = await inspectAll();
74
+ const idleAll = state1.every(b => b.lifted === 'false' && !b.filter);
75
+
76
+ // Hover infra label
77
+ const infra = state1.find(b => (b.group || '').startsWith('infra'));
78
+ await page.locator(`[data-group-label-hit="${infra.group}"]`).first().hover({ timeout: 3000 });
79
+ await page.waitForTimeout(250);
80
+ const state2 = await inspectAll();
81
+ const infra2 = state2.find(b => b.group === infra.group);
82
+ const othersFlat2 = state2.filter(b => b.group !== infra.group).every(b => b.lifted === 'false' && !b.filter);
83
+
84
+ // Mouse away → restore
85
+ await page.mouse.move(20, 20);
86
+ await page.waitForTimeout(250);
87
+ const state3 = await inspectAll();
88
+ const infra3 = state3.find(b => b.group === infra.group);
89
+
90
+ // Pin agents via CustomEvent
91
+ const agents = state1.find(b => (b.group || '').startsWith('agents'));
92
+ await page.evaluate((key) => {
93
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: key } }));
94
+ }, agents.group);
95
+ await page.waitForTimeout(250);
96
+ const state4 = await inspectAll();
97
+ const agents4 = state4.find(b => b.group === agents.group);
98
+
99
+ // Esc clears pin
100
+ await page.keyboard.press('Escape');
101
+ await page.waitForTimeout(250);
102
+ const state5 = await inspectAll();
103
+ const agents5 = state5.find(b => b.group === agents.group);
104
+
105
+ await browser.close();
106
+
107
+ const liftFilter = 'url(#topo-groupbox-lift)';
108
+ const results = {
109
+ s1_idleAllFlat: idleAll,
110
+
111
+ s2_infraLifted: infra2?.lifted === 'true',
112
+ s2_infraHasFilter: infra2?.filter === liftFilter,
113
+ s2_othersStillFlat: othersFlat2,
114
+
115
+ s3_infraRestored: infra3?.lifted === 'false' && !infra3.filter,
116
+
117
+ s4_agentsLifted: agents4?.lifted === 'true',
118
+ s4_agentsHasFilter: agents4?.filter === liftFilter,
119
+ s4_agentsPinnedAttr: agents4?.pinned === 'true',
120
+
121
+ s5_agentsCleared: agents5?.lifted === 'false' && !agents5.filter,
122
+ };
123
+ const ok = Object.values(results).every(Boolean);
124
+ console.log(`${ok ? '✅' : '❌'} groupbox hover-lift:`, JSON.stringify(results),
125
+ `\n s1=`, state1,
126
+ `\n infra2=`, infra2, ` agents4=`, agents4, ` agents5=`, agents5);
127
+ process.exit(ok ? 0 : 1);