@sleep2agi/agent-network-dashboard 0.4.6-preview.20 → 0.4.6-preview.201

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 (404) 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/01b6zl3rabau1.js +1 -0
  181. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  182. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  183. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  184. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  185. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  186. package/.next/static/chunks/0fzk-axksemic.css +2 -0
  187. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  188. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  189. package/.next/static/chunks/0n7ow3nu-_bsk.js +4 -0
  190. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  191. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  192. package/.next/static/chunks/0zx.24yobat5o.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 +3984 -264
  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-zoom-level-pop-test.mjs +122 -0
  227. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  228. package/scripts/topo-click-ripple-test.mjs +66 -0
  229. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  230. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  231. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  232. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  233. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  234. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  235. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  236. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  237. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  238. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  239. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  240. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  241. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  242. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  243. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  244. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  245. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  246. package/scripts/topo-edge-fadein-test.mjs +106 -0
  247. package/scripts/topo-edge-focus-test.mjs +113 -0
  248. package/scripts/topo-edge-hover-test.mjs +93 -0
  249. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  250. package/scripts/topo-edge-pill-test.mjs +105 -0
  251. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  252. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  253. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  254. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  255. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  256. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  257. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  258. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  259. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  260. package/scripts/topo-fit-key-test.mjs +106 -0
  261. package/scripts/topo-freshness-chip-test.mjs +74 -0
  262. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  263. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  264. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  265. package/scripts/topo-group-count-test.mjs +68 -0
  266. package/scripts/topo-group-hover-test.mjs +82 -0
  267. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  268. package/scripts/topo-group-label-hover-test.mjs +119 -0
  269. package/scripts/topo-group-label-tint-test.mjs +99 -0
  270. package/scripts/topo-group-label-title-test.mjs +83 -0
  271. package/scripts/topo-group-pin-test.mjs +119 -0
  272. package/scripts/topo-group-status-pips-test.mjs +103 -0
  273. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  274. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  275. package/scripts/topo-groupbox-live-test.mjs +100 -0
  276. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  277. package/scripts/topo-groupbox-test.mjs +4 -2
  278. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  279. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  280. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  281. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  282. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  283. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  284. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  285. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  286. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  287. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  288. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  289. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  290. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  291. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  292. package/scripts/topo-hub-a11y-test.mjs +112 -0
  293. package/scripts/topo-hub-busyness-test.mjs +100 -0
  294. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  295. package/scripts/topo-hub-fadein-test.mjs +88 -0
  296. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  297. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  298. package/scripts/topo-hub-title-test.mjs +93 -0
  299. package/scripts/topo-hub-working-count-test.mjs +99 -0
  300. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  301. package/scripts/topo-label-lift-test.mjs +119 -0
  302. package/scripts/topo-label-scale-test.mjs +78 -0
  303. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  304. package/scripts/topo-layout-key-test.mjs +91 -0
  305. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  306. package/scripts/topo-layout-toggle-test.mjs +131 -0
  307. package/scripts/topo-legend-count-test.mjs +88 -0
  308. package/scripts/topo-legend-hover-test.mjs +113 -0
  309. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  310. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  311. package/scripts/topo-legend-pin-test.mjs +128 -0
  312. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  313. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  314. package/scripts/topo-legend-row-title-test.mjs +102 -0
  315. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  316. package/scripts/topo-minimap-test.mjs +68 -0
  317. package/scripts/topo-mobile-chips-test.mjs +103 -0
  318. package/scripts/topo-node-a11y-test.mjs +107 -0
  319. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  320. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  321. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  322. package/scripts/topo-node-title-peers-test.mjs +107 -0
  323. package/scripts/topo-node-title-test.mjs +101 -0
  324. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  325. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  326. package/scripts/topo-offline-dim-test.mjs +70 -0
  327. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  328. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  329. package/scripts/topo-overlap-test.mjs +32 -5
  330. package/scripts/topo-pan-cursor-test.mjs +62 -0
  331. package/scripts/topo-panel-elevation-test.mjs +76 -0
  332. package/scripts/topo-panel-fadein-test.mjs +102 -0
  333. package/scripts/topo-panel-hot-tail-fade-test.mjs +124 -0
  334. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  335. package/scripts/topo-particle-stagger-test.mjs +82 -0
  336. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  337. package/scripts/topo-pin-intersection-test.mjs +121 -0
  338. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  339. package/scripts/topo-pin-persistence-test.mjs +105 -0
  340. package/scripts/topo-popover-meta-test.mjs +78 -0
  341. package/scripts/topo-pressure-bar-test.mjs +69 -0
  342. package/scripts/topo-pressure-hover-test.mjs +123 -0
  343. package/scripts/topo-pressure-pin-test.mjs +119 -0
  344. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  345. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  346. package/scripts/topo-pulse-rate-test.mjs +71 -0
  347. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  348. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  349. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  350. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  351. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  352. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  353. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  354. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  355. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  356. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  357. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  358. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  359. package/scripts/topo-recent-row-title-test.mjs +112 -0
  360. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  361. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  362. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  363. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  364. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  365. package/scripts/topo-reduced-motion-test.mjs +77 -0
  366. package/scripts/topo-reset-spin-test.mjs +120 -0
  367. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  368. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  369. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  370. package/scripts/topo-svg-focus-test.mjs +112 -0
  371. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  372. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  373. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  374. package/scripts/topo-tier-ring-test.mjs +69 -0
  375. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  376. package/scripts/topo-tier-stagger-test.mjs +89 -0
  377. package/scripts/topo-tz-ghost-test.mjs +69 -0
  378. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  379. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  380. package/scripts/topo-vendor-pill-test.mjs +125 -0
  381. package/scripts/topo-vendor-pin-test.mjs +125 -0
  382. package/scripts/topo-view-smooth-test.mjs +131 -0
  383. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  384. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  385. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  386. package/scripts/topo-zoom-keys-test.mjs +89 -0
  387. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  388. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  389. package/.next/static/chunks/076u9wjiilxzg.js +0 -1
  390. package/.next/static/chunks/07hlou5cndq1b.js +0 -1
  391. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  392. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  393. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  394. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  395. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  396. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  397. package/.next/static/chunks/0lw~4lr23sl-r.js +0 -1
  398. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  399. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  400. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  401. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  402. /package/.next/static/{kZOwEPGbIfBUUE4MFGvHz → IZ83xXmRRznWkvPwyxx-z}/_buildManifest.js +0 -0
  403. /package/.next/static/{kZOwEPGbIfBUUE4MFGvHz → IZ83xXmRRznWkvPwyxx-z}/_clientMiddlewareManifest.js +0 -0
  404. /package/.next/static/{kZOwEPGbIfBUUE4MFGvHz → IZ83xXmRRznWkvPwyxx-z}/_ssgManifest.js +0 -0
@@ -0,0 +1,109 @@
1
+ /* Round 162 verification: recent-signal panel header "N flows"
2
+ * tspan picks up the R160/R161 freshness vocabulary.
3
+ *
4
+ * R10 fades canvas edge by freshness (per-edge stroke opacity)
5
+ * R160 brought it to the recent-signal panel rows (per-flow pip)
6
+ * R161 brought it to the chip-row "active links" chip bullet
7
+ * (fleet aggregate in the chip-row above the SVG)
8
+ * R162 brings it to the recent-signal panel HEADER count
9
+ * (fleet aggregate inside the SVG corner)
10
+ *
11
+ * The chip-row "N active links · last 5s" and the panel-header
12
+ * "N flows" show the same metric at two scopes. R161 tinted the
13
+ * chip-row side; R162 tints the panel-side mirror so both
14
+ * scopes speak the same freshness vocabulary side-by-side.
15
+ *
16
+ * Same alpha ramp as R160/R161:
17
+ * ageSec ≤ 30 → 1.0 (fully fresh)
18
+ * 30-300s → smooth decay 1.0 → 0.25
19
+ * > 300s → 0.25 stale floor
20
+ *
21
+ * Hot tail (R129 amber " · N hot") is volume-tinted and stays
22
+ * unchanged — recency tints the head; volume colors the tail.
23
+ *
24
+ * Test: 3 page loads, 5s / 90s / 360s ago.
25
+ */
26
+ import { chromium } from 'playwright';
27
+ import { readFileSync } from 'node:fs';
28
+
29
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
30
+ const browser = await chromium.launch({ headless: true });
31
+
32
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
33
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
34
+ await ctx.addInitScript(() => {
35
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
36
+ });
37
+
38
+ async function probe(msgAgeSec) {
39
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
40
+ await ctx.unroute('**/api/hub/status*').catch(() => {});
41
+ await ctx.unroute('**/api/hub/messages*').catch(() => {});
42
+ await ctx.unroute('**/api/hub/tasks*').catch(() => {});
43
+ await ctx.route('**/api/hub/status*', async (route) => {
44
+ const r = await route.fetch();
45
+ const b = await r.json();
46
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
47
+ const mk = (alias) => ({
48
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
49
+ network_id: nid, project_dir: null,
50
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
51
+ });
52
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
53
+ });
54
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
55
+ { id: 'x', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
56
+ network_id: 'default', created_at: new Date(Date.now() - msgAgeSec * 1000).toISOString() },
57
+ ] } }));
58
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
59
+
60
+ const page = await ctx.newPage();
61
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
62
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
63
+ await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
64
+ await page.waitForTimeout(400);
65
+ const out = await page.evaluate(() => {
66
+ const el = document.querySelector('[data-recent-panel-count]');
67
+ if (!el) return null;
68
+ // SVG tspan fill attribute is the source of truth — getComputedStyle on
69
+ // tspan returns nothing useful on some browsers.
70
+ return {
71
+ alpha: parseFloat(el.getAttribute('data-recent-panel-count-freshness-alpha') || ''),
72
+ fill: el.getAttribute('fill') || '',
73
+ text: el.textContent,
74
+ };
75
+ });
76
+ await page.close();
77
+ return out;
78
+ }
79
+
80
+ const A = await probe(5); // fresh
81
+ const B = await probe(90); // mid
82
+ const C = await probe(360); // stale
83
+
84
+ await browser.close();
85
+
86
+ const cyanRgb = (str) => {
87
+ const m = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(str);
88
+ if (!m) return null;
89
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
90
+ };
91
+
92
+ const results = {
93
+ fresh_dot_found: A !== null,
94
+ mid_dot_found: B !== null,
95
+ stale_dot_found: C !== null,
96
+ fresh_alpha_1: A && Math.abs(A.alpha - 1.0) < 0.05,
97
+ mid_alpha_window: B && B.alpha > 0.7 && B.alpha < 0.95,
98
+ stale_alpha_floor: C && Math.abs(C.alpha - 0.25) < 0.05,
99
+ fresh_color_cyan: A && JSON.stringify(cyanRgb(A.fill)) === '[34,211,238]',
100
+ stale_alpha_in_fill: C && /0\.25/.test(C.fill),
101
+ ladder_descends: A && B && C && A.alpha > B.alpha && B.alpha > C.alpha,
102
+ text_says_1_flow: A && /1 flow/.test(A.text || ''),
103
+ };
104
+ const ok = Object.values(results).every(Boolean);
105
+ console.log(`${ok ? '✅' : '❌'} panel-header count freshness:`, JSON.stringify(results),
106
+ `\n fresh =`, A,
107
+ `\n mid =`, B,
108
+ `\n stale =`, C);
109
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,72 @@
1
+ /* Round 96 verification: the recent-signal panel header count now
2
+ * reads "X flows" off flowLinks.length (matching the deduped rows
3
+ * below), not "X msgs" off the raw messages.length. Pre-R96 a
4
+ * fleet with 10 messages aggregating to 3 unique pairs read
5
+ * "10 msgs" above only 3 rendered rows — read as broken.
6
+ *
7
+ * Mock 5 messages between 2 distinct pairs: alpha↔beta gets 4
8
+ * msgs (4 separate exchanges, dedupes to 1 flow), gamma→delta
9
+ * gets 1 msg (1 flow). Total = 5 msgs, 2 flows. Header should
10
+ * show "2 flows", panel renders 2 rows.
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
+
18
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
22
+ });
23
+
24
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ const r = await route.fetch();
27
+ const b = await r.json();
28
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
29
+ const mk = (alias) => ({
30
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
31
+ network_id: nid, project_dir: null,
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
33
+ });
34
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
35
+ });
36
+
37
+ const now = Date.now();
38
+ const mkMsg = (id, from_alias, to_alias, content, ageMs) => ({
39
+ id, from_alias, to_alias, content, network_id: 'default',
40
+ created_at: new Date(now - ageMs).toISOString(),
41
+ });
42
+ // 5 messages → 2 flow pairs (alpha→beta × 4, gamma→delta × 1).
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
44
+ mkMsg('m1', 'alpha', 'beta', 'ping-1', 60 * 1000),
45
+ mkMsg('m2', 'alpha', 'beta', 'ping-2', 50 * 1000),
46
+ mkMsg('m3', 'alpha', 'beta', 'ping-3', 40 * 1000),
47
+ mkMsg('m4', 'alpha', 'beta', 'ping-4', 30 * 1000),
48
+ mkMsg('m5', 'gamma', 'delta', 'hello', 20 * 1000),
49
+ ] } }));
50
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
54
+ await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
55
+ await page.waitForTimeout(400);
56
+
57
+ const headerCount = await page.locator('[data-recent-panel-count]').textContent();
58
+ const renderedRows = await page.locator('[data-recent-row]').count();
59
+
60
+ await browser.close();
61
+
62
+ const results = {
63
+ header_saysFlowsNotMsgs: /\bflows\b/.test(headerCount || ''),
64
+ header_count2: /^2 flows/.test(headerCount || ''),
65
+ rows_match_header: renderedRows === 2,
66
+ no_msgs_word: !/\bmsgs?\b/.test(headerCount || ''),
67
+ };
68
+ const ok = Object.values(results).every(Boolean);
69
+ console.log(`${ok ? '✅' : '❌'} recent-panel count:`, JSON.stringify(results),
70
+ `\n header=`, JSON.stringify(headerCount),
71
+ `\n rows=`, renderedRows);
72
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,132 @@
1
+ /* Round 129 verification: recent-signal panel header gains a
2
+ * " · N hot" amber tail when ≥ 1 flowLink has count ≥ 10.
3
+ *
4
+ * The hot-lane convention's surfaces:
5
+ * R126: canvas midpoint count badge → amber stroke
6
+ * R127: recent-signal row count digit → amber + bold
7
+ * R129: recent-signal panel HEADER → " · N hot" amber suffix
8
+ *
9
+ * Vertical scan path now reads: header → rows → canvas. Each layer
10
+ * answers a different scope: header = "how many hot?", rows =
11
+ * "which ones?", canvas = "where on the topology?".
12
+ *
13
+ * Three states:
14
+ * A. 3 pairs, all count < 10 → header "3 flows", no hot tail
15
+ * B. 5 pairs, 2 of them count ≥ 10 → header "5 flows · 2 hot", amber tail
16
+ * C. 5 pairs, 5 of them count ≥ 10 → header "5 flows · 5 hot"
17
+ */
18
+ import { chromium } from 'playwright';
19
+ import { readFileSync } from 'node:fs';
20
+
21
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
22
+
23
+ async function probe(seedPairs) {
24
+ // seedPairs is an array of [from, to, count] tuples — each pair gets
25
+ // `count` msgs to make link.count == count after dedup.
26
+ const browser = await chromium.launch({ headless: true });
27
+ const ctx = await browser.newContext({ viewport: { width: 1400, 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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
31
+ });
32
+
33
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
34
+ const aliasSet = new Set();
35
+ for (const [a, b] of seedPairs) { aliasSet.add(a); aliasSet.add(b); }
36
+ const aliases = [...aliasSet];
37
+ await ctx.route('**/api/hub/status*', async (route) => {
38
+ const r = await route.fetch();
39
+ const b = await r.json();
40
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
41
+ const mk = (alias) => ({
42
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
43
+ network_id: nid, project_dir: null,
44
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
45
+ });
46
+ await route.fulfill({ response: r, json: { ...b, sessions: aliases.map(mk) } });
47
+ });
48
+
49
+ const now = Date.now();
50
+ const msgs = [];
51
+ let id = 0;
52
+ for (const [from, to, count] of seedPairs) {
53
+ for (let i = 0; i < count; i++) {
54
+ msgs.push({
55
+ id: `m${id++}`,
56
+ from_alias: from,
57
+ to_alias: to,
58
+ content: 'hi',
59
+ network_id: 'default',
60
+ created_at: new Date(now - (20000 + i * 500 + id * 13)).toISOString(),
61
+ });
62
+ }
63
+ }
64
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
65
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
66
+
67
+ const page = await ctx.newPage();
68
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
69
+ await page.waitForFunction((n) => document.querySelectorAll('g[data-node]').length === n, aliases.length, { timeout: 30000 });
70
+ await page.waitForTimeout(500);
71
+
72
+ const out = await page.evaluate(() => {
73
+ const count = document.querySelector('[data-recent-panel-count]');
74
+ const hot = document.querySelector('[data-recent-panel-hot-count]');
75
+ return {
76
+ countText: count?.textContent,
77
+ hotText: hot?.textContent,
78
+ hotAttr: hot?.getAttribute('data-recent-panel-hot-count'),
79
+ hotFill: hot?.getAttribute('fill'),
80
+ hotWeight: hot?.getAttribute('font-weight'),
81
+ };
82
+ });
83
+
84
+ await browser.close();
85
+ return out;
86
+ }
87
+
88
+ // State A: all warm
89
+ const a = await probe([
90
+ ['alpha', 'beta', 4],
91
+ ['gamma', 'delta', 5],
92
+ ['epsilon', 'zeta', 3],
93
+ ]);
94
+
95
+ // State B: 5 pairs, 2 hot (≥ 10)
96
+ const b = await probe([
97
+ ['alpha', 'beta', 12], // hot
98
+ ['gamma', 'delta', 15], // hot
99
+ ['epsilon', 'zeta', 4],
100
+ ['eta', 'theta', 6],
101
+ ['iota', 'kappa', 8],
102
+ ]);
103
+
104
+ // State C: 5 pairs, all 5 hot
105
+ const c = await probe([
106
+ ['alpha', 'beta', 12],
107
+ ['gamma', 'delta', 11],
108
+ ['epsilon', 'zeta', 14],
109
+ ['eta', 'theta', 13],
110
+ ['iota', 'kappa', 25],
111
+ ]);
112
+
113
+ const amber = '#fbbf24';
114
+
115
+ const results = {
116
+ a_count_3flows: a.countText === '3 flows',
117
+ a_noHotTspan: a.hotText === undefined,
118
+
119
+ b_count_5flows: b.countText === '5 flows',
120
+ b_hot_text: b.hotText === ' · 2 hot',
121
+ b_hot_attr: b.hotAttr === '2',
122
+ b_hot_amberFill: (b.hotFill || '').toLowerCase() === amber,
123
+ b_hot_boldWeight: b.hotWeight === '700',
124
+
125
+ c_count_5flows: c.countText === '5 flows',
126
+ c_hot_text: c.hotText === ' · 5 hot',
127
+ c_hot_attr: c.hotAttr === '5',
128
+ };
129
+ const ok = Object.values(results).every(Boolean);
130
+ console.log(`${ok ? '✅' : '❌'} recent-panel hot header:`, JSON.stringify(results),
131
+ `\n A=`, a, `\n B=`, b, `\n C=`, c);
132
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,127 @@
1
+ /* Round 133 verification: R128's "+N more flows" footer becomes a
2
+ * clickable nav to /messages. R128 introduced the truncation hint
3
+ * as pure metadata; R133 lets the user act on it.
4
+ *
5
+ * Three states tested:
6
+ * A. Click footer → navigation lands on /messages
7
+ * B. Hover footer → opacity rises 0.55 → 0.85, underline appears
8
+ * C. Leave hover → opacity drops back, underline gone
9
+ *
10
+ * Plus: footer carries role="link" + cursor:pointer + a tooltip
11
+ * referencing /messages, all for discoverability and a11y.
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: 1400, 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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
23
+ });
24
+
25
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
26
+ const aliases = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta'];
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) => ({
32
+ alias, status: 'working', 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: aliases.map(mk) } });
37
+ });
38
+
39
+ const now = Date.now();
40
+ const msgs = [];
41
+ // 5 distinct pairs → forces R128 footer with "+ 2 more flows"
42
+ for (let i = 0; i < 5; i++) {
43
+ msgs.push({
44
+ id: `m${i}`,
45
+ from_alias: aliases[i],
46
+ to_alias: aliases[(i + 1) % aliases.length],
47
+ content: 'hi',
48
+ network_id: 'default',
49
+ created_at: new Date(now - (20000 + i * 1000)).toISOString(),
50
+ });
51
+ }
52
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
53
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
54
+
55
+ const page = await ctx.newPage();
56
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
57
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
58
+ await page.waitForSelector('[data-recent-panel-more-nav]', { timeout: 10000 });
59
+ await page.waitForTimeout(400);
60
+
61
+ const beforeHover = await page.evaluate(() => {
62
+ const wrap = document.querySelector('[data-recent-panel-more-nav]');
63
+ const text = wrap?.querySelector('text[data-recent-panel-more]');
64
+ return {
65
+ role: wrap?.getAttribute('role'),
66
+ tabindex: wrap?.getAttribute('tabindex'),
67
+ cursor: (wrap?.getAttribute('style') || '').includes('pointer'),
68
+ title: wrap?.querySelector('title')?.textContent,
69
+ opacity: text?.getAttribute('opacity'),
70
+ underline: text?.getAttribute('text-decoration'),
71
+ moreCount: text?.getAttribute('data-recent-panel-more'),
72
+ };
73
+ });
74
+
75
+ // Hover the footer
76
+ await page.locator('[data-recent-panel-more-nav]').hover();
77
+ await page.waitForTimeout(250);
78
+ const onHover = await page.evaluate(() => {
79
+ const text = document.querySelector('[data-recent-panel-more-nav] text');
80
+ return {
81
+ opacity: text?.getAttribute('opacity'),
82
+ underline: text?.getAttribute('text-decoration'),
83
+ };
84
+ });
85
+
86
+ // Move away → restore
87
+ await page.mouse.move(10, 10);
88
+ await page.waitForTimeout(250);
89
+ const afterLeave = await page.evaluate(() => {
90
+ const text = document.querySelector('[data-recent-panel-more-nav] text');
91
+ return {
92
+ opacity: text?.getAttribute('opacity'),
93
+ underline: text?.getAttribute('text-decoration'),
94
+ };
95
+ });
96
+
97
+ // Click → expect /messages navigation
98
+ await page.locator('[data-recent-panel-more-nav]').click();
99
+ await page.waitForURL('**/messages', { timeout: 10000 });
100
+ const finalUrl = page.url();
101
+
102
+ await browser.close();
103
+
104
+ const results = {
105
+ before_roleLink: beforeHover.role === 'link',
106
+ before_tabIndex0: beforeHover.tabindex === '0',
107
+ before_cursorPointer: beforeHover.cursor,
108
+ before_titleMentionsMessages: (beforeHover.title || '').includes('/messages'),
109
+ before_moreCount2: beforeHover.moreCount === '2',
110
+ before_opacity55: beforeHover.opacity === '0.55',
111
+ before_noUnderline: beforeHover.underline === 'none' || !beforeHover.underline,
112
+
113
+ hover_opacity85: onHover.opacity === '0.85',
114
+ hover_underline: onHover.underline === 'underline',
115
+
116
+ leave_opacityRestored: afterLeave.opacity === '0.55',
117
+ leave_underlineGone: afterLeave.underline === 'none' || !afterLeave.underline,
118
+
119
+ click_navigatedToMessages: /\/messages$/.test(finalUrl),
120
+ };
121
+ const ok = Object.values(results).every(Boolean);
122
+ console.log(`${ok ? '✅' : '❌'} recent-panel more nav:`, JSON.stringify(results),
123
+ `\n before=`, beforeHover,
124
+ `\n onHover=`, onHover,
125
+ `\n afterLeave=`, afterLeave,
126
+ `\n finalUrl=`, finalUrl);
127
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,106 @@
1
+ /* Round 128 verification: "+N more flows" overflow hint in the
2
+ * recent-signal panel.
3
+ *
4
+ * The panel renders top 3 flowLinks via .slice(0, 3). When the
5
+ * fleet has > 3 active flows, the rest silently disappear — the
6
+ * R96 "X flows" header reports the total but doesn't make
7
+ * truncation explicit. R128 adds a muted italic footer
8
+ * "+ N more flow(s)"
9
+ * inside the existing panel (y=82, between row-3 baseline and the
10
+ * panel bottom at y=84), only when flowLinks.length > 3.
11
+ *
12
+ * Three states tested:
13
+ * - 5 distinct flow pairs → footer shows "+ 2 more flows"
14
+ * - exactly 3 flow pairs → footer absent
15
+ * - 0 flows (empty panel) → footer absent (R45 placeholder wins)
16
+ *
17
+ * Note: the test seeds (count) messages per pair so each unique
18
+ * (from→to) pair becomes ONE flowLink (deduped). Pairs > 3 force
19
+ * the hint; pairs ≤ 3 don't.
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
+
26
+ async function probe(pairCount) {
27
+ const browser = await chromium.launch({ headless: true });
28
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
32
+ });
33
+
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ const aliases = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta'];
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) => ({
41
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
42
+ network_id: nid, project_dir: null,
43
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
44
+ });
45
+ await route.fulfill({ response: r, json: { ...b, sessions: aliases.map(mk) } });
46
+ });
47
+
48
+ const now = Date.now();
49
+ const msgs = [];
50
+ // Make pairCount distinct (from→to) pairs, 1 msg each, ascending age
51
+ for (let i = 0; i < pairCount; i++) {
52
+ msgs.push({
53
+ id: `m${i}`,
54
+ from_alias: aliases[i],
55
+ to_alias: aliases[(i + 1) % aliases.length],
56
+ content: 'hi',
57
+ network_id: 'default',
58
+ created_at: new Date(now - (20000 + i * 1000)).toISOString(),
59
+ });
60
+ }
61
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
62
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
63
+
64
+ const page = await ctx.newPage();
65
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
66
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
67
+ await page.waitForTimeout(500);
68
+
69
+ const out = await page.evaluate(() => {
70
+ const el = document.querySelector('[data-recent-panel-more]');
71
+ const rows = document.querySelectorAll('[data-recent-row]').length;
72
+ const headerCount = document.querySelector('[data-recent-panel-count]')?.textContent;
73
+ return {
74
+ footerPresent: !!el,
75
+ footerText: el?.textContent?.trim(),
76
+ footerAttr: el?.getAttribute('data-recent-panel-more'),
77
+ rows,
78
+ headerCount,
79
+ };
80
+ });
81
+
82
+ await browser.close();
83
+ return out;
84
+ }
85
+
86
+ const five = await probe(5);
87
+ const three = await probe(3);
88
+ const zero = await probe(0);
89
+
90
+ const results = {
91
+ five_footerPresent: five.footerPresent,
92
+ five_footerText: five.footerText === '+ 2 more flows',
93
+ five_footerAttr: five.footerAttr === '2',
94
+ five_rowsCapped3: five.rows === 3,
95
+ five_headerSaysFive: five.headerCount?.startsWith('5'),
96
+
97
+ three_footerAbsent: !three.footerPresent,
98
+ three_rowsExactly3: three.rows === 3,
99
+
100
+ zero_footerAbsent: !zero.footerPresent,
101
+ zero_rowsZero: zero.rows === 0,
102
+ };
103
+ const ok = Object.values(results).every(Boolean);
104
+ console.log(`${ok ? '✅' : '❌'} recent-panel more:`, JSON.stringify(results),
105
+ `\n five=`, five, `\n three=`, three, `\n zero=`, zero);
106
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,129 @@
1
+ /* Round 189 verification: recent-signal row count tspan unified
2
+ * into one always-mounted element with fill/fontWeight gated by
3
+ * isHot. transition: fill 300ms ease-out makes the R127 hot
4
+ * threshold crossing (count >= 10) ease smoothly through the
5
+ * colour shift instead of snapping.
6
+ *
7
+ * Pre-R189 two tspans branched on isHot — different
8
+ * data-recent-row-count and data-recent-row-count-hot
9
+ * attributes; mount/unmount made fill+fontWeight snap.
10
+ *
11
+ * Test:
12
+ * Mock 3 flows side by side:
13
+ * alpha→beta: count=4 (cold) → fill unset, weight unset,
14
+ * no hot attr
15
+ * alpha→gamma: count=12 (hot) → fill=amber, weight=700,
16
+ * hot attr 'true'
17
+ * alpha→delta: count=20 (hot) → fill=amber, weight=700
18
+ * All three tspans:
19
+ * - have data-recent-row-count
20
+ * - have style.transition includes 'fill 300ms'
21
+ */
22
+ import { chromium } from 'playwright';
23
+ import { readFileSync } from 'node:fs';
24
+
25
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
26
+ const browser = await chromium.launch({ headless: true });
27
+
28
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
32
+ });
33
+
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [
45
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
46
+ ] } });
47
+ });
48
+
49
+ const now = Date.now();
50
+ const msgs = [];
51
+ // alpha→beta: 4 (cold)
52
+ for (let i = 0; i < 4; i++) {
53
+ msgs.push({ id: `cold${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
54
+ network_id: 'default', created_at: new Date(now - (5000 + i * 50)).toISOString() });
55
+ }
56
+ // alpha→gamma: 12 (hot)
57
+ for (let i = 0; i < 12; i++) {
58
+ msgs.push({ id: `h1${i}`, from_alias: 'alpha', to_alias: 'gamma', content: 'hi',
59
+ network_id: 'default', created_at: new Date(now - (10000 + i * 50)).toISOString() });
60
+ }
61
+ // alpha→delta: 20 (hot)
62
+ for (let i = 0; i < 20; i++) {
63
+ msgs.push({ id: `h2${i}`, from_alias: 'alpha', to_alias: 'delta', content: 'hi',
64
+ network_id: 'default', created_at: new Date(now - (15000 + i * 50)).toISOString() });
65
+ }
66
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
67
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
68
+
69
+ const page = await ctx.newPage();
70
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
71
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
72
+ await page.waitForSelector('[data-recent-row-count]', { timeout: 10000 });
73
+ await page.waitForTimeout(400);
74
+
75
+ const probe = await page.evaluate(() => {
76
+ const tspans = [...document.querySelectorAll('[data-recent-row-count]')];
77
+ return tspans.map(t => {
78
+ const row = t.closest('[data-recent-row]');
79
+ return {
80
+ rowKey: row?.getAttribute('data-recent-row'),
81
+ text: t.textContent,
82
+ fill: t.getAttribute('fill'),
83
+ fontWeight: t.getAttribute('font-weight'),
84
+ hotAttr: t.getAttribute('data-recent-row-count-hot'),
85
+ transition: t.style.transition || getComputedStyle(t).transition,
86
+ };
87
+ });
88
+ });
89
+
90
+ await browser.close();
91
+
92
+ const findByKey = (key) => probe.find(p => p.rowKey === key);
93
+ const cold = findByKey('alpha->beta');
94
+ const hot1 = findByKey('alpha->gamma');
95
+ const hot2 = findByKey('alpha->delta');
96
+
97
+ const hasFillTransition = (s) =>
98
+ (s?.transition || '').includes('fill 300ms') ||
99
+ /fill\s+0\.3s|fill\s+300ms/.test(s?.transition || '');
100
+
101
+ const results = {
102
+ three_tspans_present: probe.length === 3,
103
+ cold_found: cold !== undefined,
104
+ hot1_found: hot1 !== undefined,
105
+ hot2_found: hot2 !== undefined,
106
+
107
+ cold_no_fill: cold?.fill === null || cold?.fill === undefined,
108
+ cold_no_fontWeight: cold?.fontWeight === null || cold?.fontWeight === undefined,
109
+ cold_no_hot_attr: cold?.hotAttr === null,
110
+ cold_text_4: cold?.text === '4',
111
+
112
+ hot1_fill_amber: hot1?.fill === '#fbbf24',
113
+ hot1_fontWeight_700: hot1?.fontWeight === '700',
114
+ hot1_hot_attr: hot1?.hotAttr === 'true',
115
+ hot1_text_12: hot1?.text === '12',
116
+
117
+ hot2_fill_amber: hot2?.fill === '#fbbf24',
118
+ hot2_fontWeight_700: hot2?.fontWeight === '700',
119
+ hot2_hot_attr: hot2?.hotAttr === 'true',
120
+ hot2_text_20: hot2?.text === '20',
121
+
122
+ all_have_fill_transition: probe.every(p => hasFillTransition(p)),
123
+ };
124
+ const ok = Object.values(results).every(Boolean);
125
+ console.log(`${ok ? '✅' : '❌'} recent-row count fill:`, JSON.stringify(results),
126
+ `\n cold:`, cold,
127
+ `\n hot1:`, hot1,
128
+ `\n hot2:`, hot2);
129
+ process.exit(ok ? 0 : 1);