@sleep2agi/agent-network-dashboard 0.4.6-preview.22 → 0.4.6-preview.221

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 (423) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +41 -41
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +6 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +14 -14
  27. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/servers/route.js +7 -0
  37. package/.next/server/app/api/hub/servers/route.js.map +5 -0
  38. package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +14 -14
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +2 -2
  49. package/.next/server/app/login.rsc +14 -14
  50. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +14 -14
  59. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/messages.html +2 -2
  67. package/.next/server/app/messages.rsc +14 -14
  68. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  74. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/node.html +2 -2
  76. package/.next/server/app/node.rsc +14 -14
  77. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  83. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/nodes.html +2 -2
  85. package/.next/server/app/nodes.rsc +14 -14
  86. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  92. package/.next/server/app/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/server-logs.html +2 -2
  95. package/.next/server/app/server-logs.rsc +14 -14
  96. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  97. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  99. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/networks.html +2 -2
  104. package/.next/server/app/settings/networks.rsc +14 -14
  105. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  106. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  112. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  114. package/.next/server/app/settings/tokens.html +2 -2
  115. package/.next/server/app/settings/tokens.rsc +14 -14
  116. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  117. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  119. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  122. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/settings.html +2 -2
  124. package/.next/server/app/settings.rsc +14 -14
  125. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  131. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  132. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/tasks.html +2 -2
  134. package/.next/server/app/tasks.rsc +14 -14
  135. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  136. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  137. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  138. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  140. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  141. package/.next/server/app-paths-manifest.json +1 -0
  142. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
  143. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
  144. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
  145. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
  146. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  147. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  148. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  149. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  150. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  151. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  160. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  174. package/.next/server/middleware-build-manifest.js +3 -3
  175. package/.next/server/pages/404.html +2 -2
  176. package/.next/server/pages/500.html +1 -1
  177. package/.next/server/server-reference-manifest.js +1 -1
  178. package/.next/server/server-reference-manifest.json +1 -1
  179. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  180. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  181. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  182. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  183. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  184. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  185. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  186. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  187. package/.next/static/chunks/0i_acp5c7_kc5.js +4 -0
  188. package/.next/static/chunks/0j537s3zamtja.js +1 -0
  189. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  190. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  191. package/.next/static/chunks/0~k1yvhakbob6.css +2 -0
  192. package/.next/static/chunks/14_8w4~_61h5q.js +1 -0
  193. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  194. package/.next/trace +2 -2
  195. package/.next/trace-build +1 -1
  196. package/.next/types/routes.d.ts +2 -1
  197. package/.next/types/validator.ts +9 -0
  198. package/app/api/hub/servers/route.ts +39 -0
  199. package/app/components/ChatPopover.tsx +38 -2
  200. package/app/components/CommandPalette.tsx +107 -0
  201. package/app/components/HelpOverlay.tsx +126 -0
  202. package/app/components/ServersDrawer.tsx +64 -27
  203. package/app/components/TopoGraph.tsx +4403 -256
  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-empty-recede-test.mjs +125 -0
  216. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  217. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  218. package/scripts/topo-arrival-ping-test.mjs +87 -0
  219. package/scripts/topo-arrow-tier-test.mjs +61 -0
  220. package/scripts/topo-auto-fit-test.mjs +73 -0
  221. package/scripts/topo-canvas-aria-test.mjs +108 -0
  222. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  223. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  224. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  225. package/scripts/topo-chip-focus-test.mjs +95 -0
  226. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  227. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  228. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  229. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  230. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  231. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  232. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  233. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  234. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  235. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  236. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  237. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  238. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  239. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  240. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  241. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  242. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  243. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  244. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  245. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  246. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  247. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  248. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  249. package/scripts/topo-edge-fadein-test.mjs +106 -0
  250. package/scripts/topo-edge-focus-test.mjs +113 -0
  251. package/scripts/topo-edge-hover-test.mjs +93 -0
  252. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  253. package/scripts/topo-edge-pill-test.mjs +105 -0
  254. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  255. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  256. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  257. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  258. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  259. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  260. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  261. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  262. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  263. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  264. package/scripts/topo-fit-key-test.mjs +106 -0
  265. package/scripts/topo-freshness-chip-test.mjs +74 -0
  266. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  267. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  268. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  269. package/scripts/topo-group-count-test.mjs +68 -0
  270. package/scripts/topo-group-hover-test.mjs +82 -0
  271. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  272. package/scripts/topo-group-label-hover-test.mjs +119 -0
  273. package/scripts/topo-group-label-tint-test.mjs +99 -0
  274. package/scripts/topo-group-label-title-test.mjs +83 -0
  275. package/scripts/topo-group-pin-test.mjs +119 -0
  276. package/scripts/topo-group-pip-fadein-test.mjs +100 -0
  277. package/scripts/topo-group-status-pips-test.mjs +103 -0
  278. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  279. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  280. package/scripts/topo-groupbox-live-test.mjs +100 -0
  281. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  282. package/scripts/topo-groupbox-test.mjs +4 -2
  283. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  284. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  285. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  286. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  287. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  288. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  289. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  290. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  291. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  292. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  293. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  294. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  295. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  296. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  297. package/scripts/topo-hub-a11y-test.mjs +112 -0
  298. package/scripts/topo-hub-busyness-test.mjs +100 -0
  299. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  300. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  301. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  302. package/scripts/topo-hub-fadein-test.mjs +88 -0
  303. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  304. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  305. package/scripts/topo-hub-title-test.mjs +93 -0
  306. package/scripts/topo-hub-working-count-test.mjs +99 -0
  307. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  308. package/scripts/topo-label-lift-test.mjs +119 -0
  309. package/scripts/topo-label-scale-test.mjs +78 -0
  310. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  311. package/scripts/topo-layout-key-test.mjs +91 -0
  312. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  313. package/scripts/topo-layout-toggle-test.mjs +131 -0
  314. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  315. package/scripts/topo-legend-count-test.mjs +88 -0
  316. package/scripts/topo-legend-hover-test.mjs +113 -0
  317. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  318. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  319. package/scripts/topo-legend-pin-test.mjs +128 -0
  320. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  321. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  322. package/scripts/topo-legend-row-title-test.mjs +102 -0
  323. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  324. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  325. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  326. package/scripts/topo-minimap-test.mjs +68 -0
  327. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  328. package/scripts/topo-mobile-chips-test.mjs +103 -0
  329. package/scripts/topo-node-a11y-test.mjs +107 -0
  330. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  331. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  332. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  333. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  334. package/scripts/topo-node-title-peers-test.mjs +107 -0
  335. package/scripts/topo-node-title-test.mjs +101 -0
  336. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  337. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  338. package/scripts/topo-offline-dim-test.mjs +70 -0
  339. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  340. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  341. package/scripts/topo-overlap-test.mjs +32 -5
  342. package/scripts/topo-pan-cursor-test.mjs +62 -0
  343. package/scripts/topo-panel-elevation-test.mjs +76 -0
  344. package/scripts/topo-panel-fadein-test.mjs +102 -0
  345. package/scripts/topo-panel-hot-tail-fade-test.mjs +124 -0
  346. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  347. package/scripts/topo-particle-stagger-test.mjs +82 -0
  348. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  349. package/scripts/topo-pin-intersection-test.mjs +121 -0
  350. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  351. package/scripts/topo-pin-persistence-test.mjs +105 -0
  352. package/scripts/topo-popover-meta-test.mjs +78 -0
  353. package/scripts/topo-pressure-bar-test.mjs +69 -0
  354. package/scripts/topo-pressure-hover-test.mjs +123 -0
  355. package/scripts/topo-pressure-pin-test.mjs +119 -0
  356. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  357. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  358. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  359. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  360. package/scripts/topo-pulse-rate-test.mjs +71 -0
  361. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  362. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  363. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  364. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  365. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  366. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  367. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  368. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  369. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  370. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  371. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  372. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  373. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  374. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  375. package/scripts/topo-recent-row-title-test.mjs +112 -0
  376. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  377. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  378. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  379. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  380. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  381. package/scripts/topo-reduced-motion-test.mjs +77 -0
  382. package/scripts/topo-reset-spin-test.mjs +120 -0
  383. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  384. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  385. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  386. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  387. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  388. package/scripts/topo-svg-focus-test.mjs +112 -0
  389. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  390. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  391. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  392. package/scripts/topo-tier-ring-test.mjs +69 -0
  393. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  394. package/scripts/topo-tier-stagger-test.mjs +89 -0
  395. package/scripts/topo-tz-ghost-test.mjs +69 -0
  396. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  397. package/scripts/topo-vendor-chip-self-hover-test.mjs +130 -0
  398. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  399. package/scripts/topo-vendor-pill-test.mjs +125 -0
  400. package/scripts/topo-vendor-pin-test.mjs +125 -0
  401. package/scripts/topo-view-smooth-test.mjs +131 -0
  402. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  403. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  404. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  405. package/scripts/topo-zoom-keys-test.mjs +89 -0
  406. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  407. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  408. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  409. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  410. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  411. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  412. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  413. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  414. package/.next/static/chunks/0n0t~-gcuxjal.js +0 -1
  415. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  416. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  417. package/.next/static/chunks/0ztnqa~vzxbbs.js +0 -1
  418. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  419. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  420. package/.next/static/chunks/17jybqi.k7lrd.js +0 -1
  421. /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_buildManifest.js +0 -0
  422. /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_clientMiddlewareManifest.js +0 -0
  423. /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_ssgManifest.js +0 -0
@@ -0,0 +1,101 @@
1
+ /* Round 177 verification: hub hover ring picks up the
2
+ * lift-on-hover gesture (r=14 → r=17), 7th surface in the
3
+ * hover-elevation family.
4
+ *
5
+ * Pre-R177 R115 hover ring at r=14 faded opacity 0→0.7 on
6
+ * hover but stayed static size. R177 adds r=14→17 on hover
7
+ * — same vocabulary R164 uses for edge badges (r=9→10.5).
8
+ *
9
+ * Test:
10
+ * 1. Mock 3 sessions in ring layout
11
+ * 2. Probe [data-topo-hub-hover-ring] idle: r=14, opacity=0
12
+ * 3. Hover the hub <g>
13
+ * 4. Re-probe: r=17, opacity=0.7 (cyber)
14
+ * 5. Mouse away
15
+ * 6. Re-probe: r=14, opacity=0
16
+ * 7. Transition style includes both opacity AND r
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 {
28
+ localStorage.setItem('anet-theme', 'cyber');
29
+ sessionStorage.setItem('anet_v3_auth', '1');
30
+ localStorage.setItem('anet-topo-layout', 'ring');
31
+ } catch {}
32
+ });
33
+
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
47
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
48
+
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
52
+ await page.waitForSelector('[data-topo-hub-hover-ring]', { timeout: 10000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ const probe = () => page.evaluate(() => {
56
+ const el = document.querySelector('[data-topo-hub-hover-ring]');
57
+ if (!el) return null;
58
+ return {
59
+ r: parseFloat(el.getAttribute('r') || ''),
60
+ opacity: parseFloat(el.getAttribute('opacity') || ''),
61
+ radiusAttr: parseFloat(el.getAttribute('data-topo-hub-hover-ring-radius') || ''),
62
+ transition: el.style.transition || getComputedStyle(el).transition,
63
+ };
64
+ });
65
+
66
+ const idle = await probe();
67
+
68
+ // Hover the hub
69
+ await page.locator('[data-topo-hub]').hover();
70
+ await page.waitForTimeout(250);
71
+ const hovered = await probe();
72
+
73
+ // Move cursor away
74
+ await page.mouse.move(10, 10);
75
+ await page.waitForTimeout(250);
76
+ const idleAgain = await probe();
77
+
78
+ await browser.close();
79
+
80
+ const results = {
81
+ ring_found: idle !== null,
82
+ idle_r_14: idle && Math.abs(idle.r - 14) < 0.01,
83
+ idle_opacity_0: idle && idle.opacity === 0,
84
+ idle_radius_attr_14: idle && idle.radiusAttr === 14,
85
+
86
+ hover_r_17: hovered && Math.abs(hovered.r - 17) < 0.01,
87
+ hover_opacity_0p7: hovered && Math.abs(hovered.opacity - 0.7) < 0.01,
88
+ hover_radius_attr_17: hovered && hovered.radiusAttr === 17,
89
+
90
+ idle_again_r_14: idleAgain && Math.abs(idleAgain.r - 14) < 0.01,
91
+ idle_again_opacity_0: idleAgain && idleAgain.opacity === 0,
92
+
93
+ transition_has_opacity: (idle?.transition || '').match(/opacity\s+(180ms|0\.18s)/),
94
+ transition_has_r: (idle?.transition || '').match(/(^|\s|,)\s*r\s+(180ms|0\.18s)/),
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} hub hover lift:`, JSON.stringify(Object.fromEntries(Object.entries(results).map(([k,v]) => [k, !!v]))),
98
+ `\n idle =`, idle,
99
+ `\n hovered =`, hovered,
100
+ `\n idle2 =`, idleAgain);
101
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,82 @@
1
+ /* Round 115 verification: hub center hover-state visual feedback.
2
+ * R52 made the hub clickable (fit view) + cursor:pointer, but
3
+ * hovering showed nothing. Now a thin r=14 stroke ring fades in on
4
+ * hover and out on leave. Sits inside the existing r=18 halo so
5
+ * overlap footprint unchanged.
6
+ *
7
+ * Probes:
8
+ * - baseline: ring opacity 0, data-topo-hub-hovered="false"
9
+ * - hover hub: ring opacity > 0, hovered="true"
10
+ * - mouseleave: opacity 0, hovered="false"
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 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', 'ring');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ } catch {}
25
+ });
26
+
27
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const 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')] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
44
+ await page.waitForSelector('[data-topo-hub]', { timeout: 10000 });
45
+ await page.waitForTimeout(400);
46
+
47
+ const read = () => page.evaluate(() => {
48
+ const hub = document.querySelector('[data-topo-hub]');
49
+ const ring = document.querySelector('[data-topo-hub-hover-ring]');
50
+ return {
51
+ hovered: hub?.getAttribute('data-topo-hub-hovered') || '',
52
+ ringOpacity: +(ring?.getAttribute('opacity') || '0'),
53
+ };
54
+ });
55
+
56
+ const before = await read();
57
+
58
+ await page.locator('[data-topo-hub]').hover();
59
+ await page.waitForTimeout(300);
60
+ const onHover = await read();
61
+
62
+ // Move pointer far away.
63
+ await page.mouse.move(10, 10);
64
+ await page.waitForTimeout(300);
65
+ const afterLeave = await read();
66
+
67
+ await browser.close();
68
+
69
+ const results = {
70
+ before_notHovered: before.hovered === 'false',
71
+ before_ringHidden: before.ringOpacity === 0,
72
+ hover_attrTrue: onHover.hovered === 'true',
73
+ hover_ringVisible: onHover.ringOpacity > 0.5,
74
+ leave_attrFalse: afterLeave.hovered === 'false',
75
+ leave_ringHidden: afterLeave.ringOpacity === 0,
76
+ };
77
+ const ok = Object.values(results).every(Boolean);
78
+ console.log(`${ok ? '✅' : '❌'} hub-hover ring:`, JSON.stringify(results),
79
+ `\n before=`, before,
80
+ `\n onHover=`, onHover,
81
+ `\n afterLeave=`, afterLeave);
82
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,93 @@
1
+ /* Round 43 verification: central hub carries a `<title>` summary in ring
2
+ * layout. Assertions:
3
+ * - hub renders in ring layout (data-topo-hub group present)
4
+ * - <title> reads "Network hub · N sessions · …" with the right counts
5
+ * - zero-count clauses (online/working/links) drop cleanly
6
+ * - grid layout doesn't render the hub at all */
7
+ import { chromium } from 'playwright';
8
+ import { readFileSync } from 'node:fs';
9
+
10
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
11
+ const browser = await chromium.launch({ headless: true });
12
+
13
+ async function probe({ layout, sessionsSpec, msgs = [] }) {
14
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
15
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
16
+ await ctx.addInitScript((lay) => {
17
+ try {
18
+ localStorage.setItem('anet-theme', 'cyber');
19
+ localStorage.setItem('anet-topo-layout', lay);
20
+ sessionStorage.setItem('anet_v3_auth', '1');
21
+ } catch {}
22
+ }, layout);
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+ await ctx.route('**/api/hub/status*', async (route) => {
25
+ const r = await route.fetch();
26
+ const b = await r.json();
27
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
28
+ const sessions = sessionsSpec.map(s => ({
29
+ alias: s.alias, status: s.status, network_id: nid, project_dir: null,
30
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
31
+ }));
32
+ await route.fulfill({ response: r, json: { ...b, sessions } });
33
+ });
34
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
35
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
36
+ const page = await ctx.newPage();
37
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
38
+ await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
39
+ await page.waitForTimeout(500);
40
+ const data = await page.evaluate(() => {
41
+ const hub = document.querySelector('g[data-topo-hub]');
42
+ const title = hub?.querySelector('title');
43
+ return { hubPresent: !!hub, titleText: title?.textContent || null };
44
+ });
45
+ await ctx.close();
46
+ return data;
47
+ }
48
+
49
+ // Ring + mixed-status fleet → full summary line
50
+ const ringFull = await probe({
51
+ layout: 'ring',
52
+ sessionsSpec: [
53
+ { alias: 'a', status: 'working' },
54
+ { alias: 'b', status: 'idle' },
55
+ { alias: 'c', status: 'idle' },
56
+ { alias: 'd', status: 'offline' },
57
+ ],
58
+ msgs: [
59
+ { from_alias: 'a', to_alias: 'b', content: 'm', created_at: new Date().toISOString() },
60
+ ],
61
+ });
62
+ // Ring + all-offline → online/working/links clauses drop
63
+ const ringOfflineOnly = await probe({
64
+ layout: 'ring',
65
+ sessionsSpec: [
66
+ { alias: 'x', status: 'offline' },
67
+ { alias: 'y', status: 'offline' },
68
+ ],
69
+ });
70
+ // Grid → no hub at all
71
+ const grid = await probe({
72
+ layout: 'grid',
73
+ sessionsSpec: [{ alias: 'a', status: 'idle' }, { alias: 'b', status: 'idle' }],
74
+ });
75
+
76
+ await browser.close();
77
+ // R52 appended "\nclick to fit view" as a second line to the hub
78
+ // <title> so the click-to-fit affordance is discoverable on hover.
79
+ // Compare on the first line only; assert the hint is present too.
80
+ const firstLine = (s) => (s || '').split('\n')[0];
81
+ const results = {
82
+ ringHubPresent: ringFull.hubPresent === true,
83
+ ringFullSummary: firstLine(ringFull.titleText) === 'Network hub · 4 sessions · 3 online · 1 working · 1 active link',
84
+ ringOfflineSummary: ringOfflineOnly.hubPresent === true && firstLine(ringOfflineOnly.titleText) === 'Network hub · 2 sessions',
85
+ ringHintsAtFit: /click to fit view/i.test(ringFull.titleText || ''),
86
+ gridHasNoHub: grid.hubPresent === false,
87
+ };
88
+ const ok = Object.values(results).every(Boolean);
89
+ console.log(`${ok ? '✅' : '❌'} hub title:`, JSON.stringify(results),
90
+ `\n ringFull=${JSON.stringify(ringFull.titleText)}`,
91
+ `\n ringOffline=${JSON.stringify(ringOfflineOnly.titleText)}`,
92
+ `\n gridHub=${grid.hubPresent}`);
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,99 @@
1
+ /* Round 130 verification: hub center shows workingCount digit when
2
+ * > 0. The R84 busyness breath already encodes workingCount through
3
+ * motion (peak amplitude + period both vary with count buckets);
4
+ * R130 adds a second channel — the digit itself — at the canvas's
5
+ * focal point. A glance at the hub now answers two questions
6
+ * simultaneously: "is the fleet busy" (motion) and "how busy" (number).
7
+ *
8
+ * Layout invariant: workingCount=0 falls through to the existing
9
+ * decorative highlight (data-topo-hub-highlight); workingCount>0
10
+ * swaps it for the digit text (data-topo-hub-working-count={N}).
11
+ *
12
+ * Three states:
13
+ * A. all idle (0 working) → decorative highlight rendered,
14
+ * no digit text
15
+ * B. mix (3 working) → digit "3" rendered, no highlight
16
+ * C. all working (5 working) → digit "5" rendered, no highlight
17
+ *
18
+ * Additional assertion: hub click-to-fit (R52) still works when
19
+ * the digit is rendered — the text's pointerEvents:none means the
20
+ * digit can't intercept clicks meant for the hub <g>.
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
+
27
+ async function probe(workingCount, totalCount = 5) {
28
+ const browser = await chromium.launch({ headless: true });
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
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const sessions = [];
40
+ for (let i = 0; i < totalCount; i++) {
41
+ sessions.push({
42
+ alias: `node${i}`,
43
+ status: i < workingCount ? 'working' : 'idle',
44
+ 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
+ }
49
+ await route.fulfill({ response: r, json: { ...b, sessions } });
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((n) => document.querySelectorAll('g[data-node]').length === n, totalCount, { timeout: 30000 });
57
+ await page.waitForTimeout(400);
58
+
59
+ const out = await page.evaluate(() => {
60
+ const digit = document.querySelector('[data-topo-hub-working-count]');
61
+ const highlight = document.querySelector('[data-topo-hub-highlight]');
62
+ return {
63
+ digitPresent: !!digit,
64
+ digitText: digit?.textContent,
65
+ digitAttr: digit?.getAttribute('data-topo-hub-working-count'),
66
+ digitPointerEv: digit?.getAttribute('style')?.includes('pointer-events: none'),
67
+ highlightPresent: !!highlight,
68
+ };
69
+ });
70
+
71
+ await browser.close();
72
+ return out;
73
+ }
74
+
75
+ const a = await probe(0, 3);
76
+ const b = await probe(3, 5);
77
+ const c = await probe(5, 5);
78
+
79
+ const results = {
80
+ a_noDigit: !a.digitPresent,
81
+ a_hasHighlight: a.highlightPresent,
82
+
83
+ b_hasDigit: b.digitPresent,
84
+ b_digitText3: b.digitText === '3',
85
+ b_digitAttr3: b.digitAttr === '3',
86
+ b_pointerNone: b.digitPointerEv === true,
87
+ b_noHighlight: !b.highlightPresent,
88
+
89
+ c_hasDigit: c.digitPresent,
90
+ c_digitText5: c.digitText === '5',
91
+ c_digitAttr5: c.digitAttr === '5',
92
+ c_noHighlight: !c.highlightPresent,
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} hub working count:`, JSON.stringify(results),
96
+ `\n A(0 working)=`, a,
97
+ `\n B(3 working)=`, b,
98
+ `\n C(5 working)=`, c);
99
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 53 verification: edges between two members of the hovered prefix
2
+ * group brighten to 1.3× even if neither endpoint is the exact hovered
3
+ * alias. Edges leaving the team still dim per R40.
4
+ *
5
+ * Sessions:
6
+ * alpha1, alpha2, alpha3 — share prefix "alpha" → same group
7
+ * beta — singleton
8
+ * Messages:
9
+ * alpha1 → alpha2 (touches alpha1 directly → 1.7× per R40)
10
+ * alpha2 → alpha3 (BOTH in alpha group but neither is hovered → 1.3× per R53)
11
+ * alpha1 → beta (touches alpha1, beta is outside → 1.7× per R40)
12
+ * alpha2 → beta (in-group endpoint, but beta is outside → 0.35× per R40)
13
+ *
14
+ * Hover alpha1 and read the visible flow paths' opacity to validate the
15
+ * 4-tier ladder.
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
21
+ const browser = await chromium.launch({ headless: true });
22
+
23
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
24
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
25
+ await ctx.addInitScript(() => {
26
+ try {
27
+ localStorage.setItem('anet-theme', 'cyber');
28
+ localStorage.setItem('anet-topo-layout', 'grid');
29
+ sessionStorage.setItem('anet_v3_auth', '1');
30
+ } catch {}
31
+ });
32
+
33
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
34
+ await ctx.route('**/api/hub/status*', async (route) => {
35
+ const r = await route.fetch();
36
+ const b = await r.json();
37
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
38
+ const sessions = ['alpha1', 'alpha2', 'alpha3', 'beta'].map(a => ({
39
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
40
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
41
+ }));
42
+ await route.fulfill({ response: r, json: { ...b, sessions } });
43
+ });
44
+ const now = new Date().toISOString();
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
46
+ { from_alias: 'alpha1', to_alias: 'alpha2', content: 'm', created_at: now },
47
+ { from_alias: 'alpha2', to_alias: 'alpha3', content: 'm', created_at: now },
48
+ { from_alias: 'alpha1', to_alias: 'beta', content: 'm', created_at: now },
49
+ { from_alias: 'alpha2', to_alias: 'beta', content: 'm', created_at: now },
50
+ ] } }));
51
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
52
+ const page = await ctx.newPage();
53
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
54
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
55
+ await page.waitForTimeout(600);
56
+
57
+ const readEdgeOpacities = () => page.evaluate(() => {
58
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
59
+ const out = {};
60
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
61
+ const t = g.querySelector('path[data-edge-hitbox] title');
62
+ if (!t) continue;
63
+ const route = (t.textContent || '').split('\n')[0];
64
+ const base = [...g.querySelectorAll(':scope > path')].find(
65
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
66
+ );
67
+ if (base) out[route] = +base.getAttribute('opacity');
68
+ }
69
+ return out;
70
+ });
71
+
72
+ const before = await readEdgeOpacities();
73
+
74
+ // Hover alpha1.
75
+ await page.locator('g[data-node="alpha1"]').hover();
76
+ await page.waitForTimeout(300);
77
+ const after = await readEdgeOpacities();
78
+
79
+ await browser.close();
80
+
81
+ const get = (m, r) => m[r];
82
+ const baseRef = get(before, 'alpha1 → alpha2'); // pick any
83
+ const exactTouch = get(after, 'alpha1 → alpha2'); // R40 1.7×
84
+ const inGroupOnly = get(after, 'alpha2 → alpha3'); // R53 1.3×
85
+ const externalTouch = get(after, 'alpha1 → beta'); // R40 1.7×
86
+ const externalAway = get(after, 'alpha2 → beta'); // R40 0.35×
87
+
88
+ const results = {
89
+ baselineAllEqual: Object.values(before).every(o => Math.abs(o - baseRef) < 0.001),
90
+ exactTouchBrightest: exactTouch > baseRef * 1.5,
91
+ inGroupBoosted: inGroupOnly > baseRef * 1.15 && inGroupOnly < exactTouch,
92
+ externalEndpointStillBright: externalTouch > baseRef * 1.5,
93
+ externalDims: externalAway < baseRef * 0.5,
94
+ hierarchyOrdered: exactTouch > inGroupOnly && inGroupOnly > externalAway,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} in-group edge boost:`, JSON.stringify(results),
98
+ `\n baseline=${baseRef}`,
99
+ `\n α1→α2 (exact)=${exactTouch}`,
100
+ `\n α2→α3 (in-group)=${inGroupOnly}`,
101
+ `\n α1→β (exact, out)=${externalTouch}`,
102
+ `\n α2→β (leaving)=${externalAway}`);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,119 @@
1
+ /* Round 26 verification: hovering a node lifts its label group up by
2
+ * 1.5 px (CSS transform stacked on the SVG positioning translate). Both
3
+ * the full-card path (sparse fleet) and the dense plain-text path
4
+ * (large fleet) should respond. */
5
+ import { chromium } from 'playwright';
6
+ import { readFileSync } from 'node:fs';
7
+
8
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
9
+ const browser = await chromium.launch({ headless: true });
10
+
11
+ async function probe(fleetSize) {
12
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
13
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
14
+ await ctx.addInitScript(() => {
15
+ try {
16
+ localStorage.setItem('anet-theme', 'cyber');
17
+ localStorage.setItem('anet-topo-layout', 'grid');
18
+ localStorage.setItem('anet-topo-nodescale', '1');
19
+ sessionStorage.setItem('anet_v3_auth', '1');
20
+ } catch {}
21
+ });
22
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
23
+ await ctx.route('**/api/hub/status*', async (route) => {
24
+ const r = await route.fetch();
25
+ const b = await r.json();
26
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
27
+ const sessions = Array.from({ length: fleetSize }, (_, i) => ({
28
+ alias: `lab${i + 1}`, status: 'idle', network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ }));
31
+ await route.fulfill({ response: r, json: { ...b, sessions } });
32
+ });
33
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
34
+ const page = await ctx.newPage();
35
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
36
+ await page.waitForFunction((n) => {
37
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
38
+ return !!svg && svg.querySelectorAll('g[data-node]').length === n;
39
+ }, fleetSize, { timeout: 30000 }).catch(() => {});
40
+ await page.waitForTimeout(500);
41
+
42
+ // Re-query the label element fresh each sample — React re-renders on
43
+ // hover (hoveredAlias state change) so any element handle captured
44
+ // before the hover would be detached/stale by the time we read its
45
+ // post-hover position.
46
+ const readLabelTop = () => page.evaluate(() => {
47
+ const node = document.querySelector('g[data-node="lab1"]');
48
+ if (!node) return null;
49
+ const full = [...node.querySelectorAll('g')].find(g => /group-hover:-translate-y/.test(g.getAttribute('class') || ''));
50
+ const el = full || [...node.querySelectorAll('text')].find(t => /group-hover:-translate-y/.test(t.getAttribute('class') || ''));
51
+ return el ? el.getBoundingClientRect().top : null;
52
+ });
53
+
54
+ const beforeY = await readLabelTop();
55
+ await page.locator('g[data-node="lab1"]').hover();
56
+ await page.waitForTimeout(300);
57
+ const afterY = await readLabelTop();
58
+
59
+ await ctx.close();
60
+ return { beforeY, afterY };
61
+ }
62
+
63
+ const sparse = await probe(4);
64
+ // Note: in dense mode the same alias's hover triggers `showFullLabel`,
65
+ // which swaps the plain-text element for the full-card <g>. That path
66
+ // switch makes a same-element before/after comparison meaningless.
67
+ // The plain-text element still carries the same hover-lift class, so
68
+ // the lift wiring is verified at the DOM level via the second probe.
69
+ const denseClassPresent = await (async () => {
70
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
71
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
72
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
73
+ await ctx.addInitScript(() => {
74
+ localStorage.setItem('anet-topo-layout', 'grid');
75
+ localStorage.setItem('anet-topo-nodescale', '1');
76
+ sessionStorage.setItem('anet_v3_auth', '1');
77
+ });
78
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
79
+ await ctx.route('**/api/hub/status*', async (route) => {
80
+ const r = await route.fetch();
81
+ const b = await r.json();
82
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
83
+ const sessions = Array.from({ length: 28 }, (_, i) => ({
84
+ alias: `lab${i + 1}`, status: 'idle', network_id: nid, project_dir: null,
85
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
86
+ }));
87
+ await route.fulfill({ response: r, json: { ...b, sessions } });
88
+ });
89
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
90
+ const page = await ctx.newPage();
91
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
92
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 28, { timeout: 30000 }).catch(() => {});
93
+ await page.waitForTimeout(500);
94
+ const has = await page.evaluate(() => {
95
+ const node = document.querySelector('g[data-node="lab2"]'); // not the one we'd hover
96
+ if (!node) return false;
97
+ const t = [...node.querySelectorAll('text')].find(x => /group-hover:-translate-y/.test(x.getAttribute('class') || ''));
98
+ return !!t;
99
+ });
100
+ await ctx.close();
101
+ return has;
102
+ })();
103
+
104
+ await browser.close();
105
+
106
+ // R26 contributes label-only -1.5px lift; R51 (preview.58) adds -2px on the
107
+ // parent <g data-node> on hover, so the label's total measured movement
108
+ // when the node is hovered is ~3.5px. Widen the upper bound to 4 so the
109
+ // two lifts compose without flagging this as a regression.
110
+ const lifted = (m) => m.beforeY != null && m.afterY != null && m.beforeY - m.afterY >= 1 && m.beforeY - m.afterY <= 4;
111
+
112
+ const results = {
113
+ sparseLabelLifts: lifted(sparse),
114
+ densePlainTextHasLiftClass: denseClassPresent,
115
+ };
116
+ const ok = Object.values(results).every(Boolean);
117
+ console.log(`${ok ? '✅' : '❌'} label hover lift:`, JSON.stringify(results),
118
+ `sparse=Δ${(sparse.beforeY - sparse.afterY)?.toFixed(2)}px`);
119
+ process.exit(ok ? 0 : 1);