@sleep2agi/agent-network-dashboard 0.4.6-preview.12 → 0.4.6-preview.121

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 (327) 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 +69 -55
  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/0.hnfdve7~422.js +7 -0
  181. package/.next/static/chunks/001t1axy5kvz8.css +2 -0
  182. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  183. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  184. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  185. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  186. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  187. package/.next/static/chunks/0a-6zp8m0ux5..js +1 -0
  188. package/.next/static/chunks/0ekib0be3t9i5.js +1 -0
  189. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  190. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  191. package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
  192. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  193. package/.next/static/chunks/106~a5kttxze-.js +4 -0
  194. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  195. package/.next/trace +2 -2
  196. package/.next/trace-build +1 -1
  197. package/.next/types/routes.d.ts +2 -1
  198. package/.next/types/validator.ts +9 -0
  199. package/app/api/hub/servers/route.ts +39 -0
  200. package/app/components/AppShell.tsx +2 -0
  201. package/app/components/ChatPopover.tsx +38 -2
  202. package/app/components/CommandPalette.tsx +94 -0
  203. package/app/components/HelpOverlay.tsx +76 -0
  204. package/app/components/ServersDrawer.tsx +206 -0
  205. package/app/components/TopoGraph.tsx +2181 -163
  206. package/app/components/utils.ts +8 -7
  207. package/app/globals.css +19 -1
  208. package/app/lib/time.ts +36 -0
  209. package/package.json +1 -1
  210. package/scripts/help-overlay-topo-test.mjs +44 -0
  211. package/scripts/servers-drawer-live-test.mjs +96 -0
  212. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  213. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  216. package/scripts/topo-arrival-ping-test.mjs +87 -0
  217. package/scripts/topo-arrow-tier-test.mjs +61 -0
  218. package/scripts/topo-auto-fit-test.mjs +73 -0
  219. package/scripts/topo-chat-focus-ring-test.mjs +77 -0
  220. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  221. package/scripts/topo-click-ripple-test.mjs +66 -0
  222. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  223. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  224. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  225. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  226. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  227. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  228. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  229. package/scripts/topo-edge-count-badge-test.mjs +84 -0
  230. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  231. package/scripts/topo-edge-focus-test.mjs +113 -0
  232. package/scripts/topo-edge-hover-test.mjs +93 -0
  233. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  234. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  235. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  236. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  237. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  238. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  239. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  240. package/scripts/topo-fit-key-test.mjs +106 -0
  241. package/scripts/topo-freshness-chip-test.mjs +74 -0
  242. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  243. package/scripts/topo-group-count-test.mjs +68 -0
  244. package/scripts/topo-group-hover-test.mjs +82 -0
  245. package/scripts/topo-group-label-hover-test.mjs +119 -0
  246. package/scripts/topo-group-label-tint-test.mjs +99 -0
  247. package/scripts/topo-group-label-title-test.mjs +83 -0
  248. package/scripts/topo-group-pin-test.mjs +119 -0
  249. package/scripts/topo-group-status-pips-test.mjs +103 -0
  250. package/scripts/topo-groupbox-live-test.mjs +100 -0
  251. package/scripts/topo-groupbox-test.mjs +4 -2
  252. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  253. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  254. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  255. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  256. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  257. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  258. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  259. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  260. package/scripts/topo-hub-busyness-test.mjs +100 -0
  261. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  262. package/scripts/topo-hub-title-test.mjs +93 -0
  263. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  264. package/scripts/topo-label-lift-test.mjs +119 -0
  265. package/scripts/topo-label-scale-test.mjs +78 -0
  266. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  267. package/scripts/topo-layout-key-test.mjs +91 -0
  268. package/scripts/topo-legend-count-test.mjs +88 -0
  269. package/scripts/topo-legend-hover-test.mjs +113 -0
  270. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  271. package/scripts/topo-legend-pin-test.mjs +128 -0
  272. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  273. package/scripts/topo-minimap-test.mjs +68 -0
  274. package/scripts/topo-mobile-chips-test.mjs +103 -0
  275. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  276. package/scripts/topo-node-title-test.mjs +101 -0
  277. package/scripts/topo-offline-dim-test.mjs +70 -0
  278. package/scripts/topo-overlap-test.mjs +32 -5
  279. package/scripts/topo-pan-cursor-test.mjs +62 -0
  280. package/scripts/topo-panel-elevation-test.mjs +76 -0
  281. package/scripts/topo-particle-stagger-test.mjs +82 -0
  282. package/scripts/topo-pin-persistence-test.mjs +105 -0
  283. package/scripts/topo-popover-meta-test.mjs +78 -0
  284. package/scripts/topo-pressure-bar-test.mjs +69 -0
  285. package/scripts/topo-pressure-hover-test.mjs +123 -0
  286. package/scripts/topo-pressure-pin-test.mjs +119 -0
  287. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  288. package/scripts/topo-pulse-rate-test.mjs +71 -0
  289. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  290. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  291. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  292. package/scripts/topo-recent-row-ts-test.mjs +92 -0
  293. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  294. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  295. package/scripts/topo-reduced-motion-test.mjs +77 -0
  296. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  297. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  298. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  299. package/scripts/topo-tier-ring-test.mjs +69 -0
  300. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  301. package/scripts/topo-tier-stagger-test.mjs +89 -0
  302. package/scripts/topo-tz-ghost-test.mjs +69 -0
  303. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  304. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  305. package/scripts/topo-vendor-pill-test.mjs +125 -0
  306. package/scripts/topo-vendor-pin-test.mjs +125 -0
  307. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  308. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  309. package/scripts/topo-zoom-keys-test.mjs +89 -0
  310. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  311. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  312. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  313. package/.next/static/chunks/0cj5qr_a8dzky.js +0 -7
  314. package/.next/static/chunks/0dttftqqehpdj.js +0 -1
  315. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  316. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  317. package/.next/static/chunks/0ja71r81z59di.js +0 -1
  318. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  319. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  320. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  321. package/.next/static/chunks/0z6mgaoe8c89-.js +0 -1
  322. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  323. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  324. package/.next/static/chunks/11y8xsp9mn.m8.css +0 -2
  325. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_buildManifest.js +0 -0
  326. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_clientMiddlewareManifest.js +0 -0
  327. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_ssgManifest.js +0 -0
@@ -0,0 +1,76 @@
1
+ /* Round 57 verification: recent-signal + legend panels carry a drop-
2
+ * shadow filter so they read as floating cards instead of pasted-on
3
+ * graphics — especially important on light theme where panel fill is
4
+ * near-white on a near-white canvas.
5
+ *
6
+ * - Both panels render a <rect> with `data-topo-panel-elevation`.
7
+ * - The rect's `style.filter` contains "drop-shadow(".
8
+ * - Light vs cyber themes use different shadow alpha (light tuned for
9
+ * contrast against white canvas; cyber for subtle depth on dark).
10
+ * Verify the strings differ so the theme switch isn't a no-op.
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
+ async function probe(theme) {
19
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript((t) => {
22
+ try {
23
+ localStorage.setItem('anet-theme', t);
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ }, theme);
28
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
29
+ await ctx.route('**/api/hub/status*', async (route) => {
30
+ const r = await route.fetch();
31
+ const b = await r.json();
32
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
33
+ const sessions = ['alpha', 'beta'].map(a => ({
34
+ alias: a, status: 'idle', 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 } });
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.waitForSelector('rect[data-topo-panel-elevation="recent"]', { timeout: 30000 });
44
+ await page.waitForTimeout(400);
45
+ const filters = await page.evaluate(() => {
46
+ const recent = document.querySelector('rect[data-topo-panel-elevation="recent"]');
47
+ const legend = document.querySelector('rect[data-topo-panel-elevation="legend"]');
48
+ return {
49
+ recent: recent?.getAttribute('style') || '',
50
+ legend: legend?.getAttribute('style') || '',
51
+ recentComputed: recent ? getComputedStyle(recent).filter : null,
52
+ legendComputed: legend ? getComputedStyle(legend).filter : null,
53
+ };
54
+ });
55
+ await ctx.close();
56
+ return filters;
57
+ }
58
+
59
+ const light = await probe('light');
60
+ const cyber = await probe('cyber');
61
+ await browser.close();
62
+
63
+ const hasShadow = (s) => /drop-shadow\(/.test(s || '');
64
+ const results = {
65
+ light_recent_hasShadow: hasShadow(light.recent),
66
+ light_legend_hasShadow: hasShadow(light.legend),
67
+ cyber_recent_hasShadow: hasShadow(cyber.recent),
68
+ cyber_legend_hasShadow: hasShadow(cyber.legend),
69
+ themes_differ: light.recent !== cyber.recent,
70
+ computed_resolves: hasShadow(light.recentComputed) && hasShadow(cyber.recentComputed),
71
+ };
72
+ const ok = Object.values(results).every(Boolean);
73
+ console.log(`${ok ? '✅' : '❌'} panel elevation:`, JSON.stringify(results),
74
+ `\n light.recent=${JSON.stringify(light.recent)}`,
75
+ `\n cyber.recent=${JSON.stringify(cyber.recent)}`);
76
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,82 @@
1
+ /* Round 103 verification: flow-particle `<animateMotion begin>` is
2
+ * unique per edge, staggered by `-(index × 0.37) % duration s`. Pre-
3
+ * R103 all edges' particles fired at t=0 in lockstep; the stagger
4
+ * gives the canvas an organic "many independent rivers" feel.
5
+ *
6
+ * Verifies:
7
+ * - every edge has a `begin` attribute on its <animateMotion>
8
+ * - all begin values are distinct (no two edges share a phase)
9
+ * - the format is "-Ns" (negative offset for retroactive cycle start)
10
+ * - reducedMotion still suppresses the particle entirely
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+ const fresh = new Date(Date.now() - 30 * 1000).toISOString();
18
+
19
+ const probe = async (reducedMotion) => {
20
+ const ctx = await browser.newContext({
21
+ viewport: { width: 1400, height: 1500 },
22
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
23
+ });
24
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
25
+ await ctx.addInitScript(() => {
26
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta'), mk('epsilon')] } });
38
+ });
39
+ const now = Date.now();
40
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
41
+ id, from_alias, to_alias, content: 'hi', network_id: 'default',
42
+ created_at: new Date(now - ageMs).toISOString(),
43
+ });
44
+ // Five distinct edges → five animations.
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
46
+ mkMsg('m1', 'alpha', 'beta', 30 * 1000),
47
+ mkMsg('m2', 'gamma', 'delta', 35 * 1000),
48
+ mkMsg('m3', 'beta', 'gamma', 40 * 1000),
49
+ mkMsg('m4', 'delta', 'epsilon', 45 * 1000),
50
+ mkMsg('m5', 'epsilon', 'alpha', 50 * 1000),
51
+ ] } }));
52
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
56
+ await page.waitForTimeout(400);
57
+
58
+ const beginValues = await page.evaluate(() => {
59
+ return [...document.querySelectorAll('animateMotion')]
60
+ .map(a => a.getAttribute('begin'));
61
+ });
62
+ await ctx.close();
63
+ return beginValues;
64
+ };
65
+
66
+ const normal = await probe(false);
67
+ const reduced = await probe(true);
68
+ await browser.close();
69
+
70
+ const results = {
71
+ normal_hasAtLeast5: normal.length >= 5,
72
+ normal_allHaveBegin: normal.every(b => typeof b === 'string' && b.length > 0),
73
+ normal_allNegativeOffset: normal.every(b => /^-\d/.test(b || '')),
74
+ normal_allDistinct: new Set(normal).size === normal.length,
75
+ normal_firstIsZero: normal[0] === '-0.000s',
76
+ reduced_noParticles: reduced.length === 0,
77
+ };
78
+ const ok = Object.values(results).every(Boolean);
79
+ console.log(`${ok ? '✅' : '❌'} particle stagger:`, JSON.stringify(results),
80
+ `\n normal=`, normal,
81
+ `\n reduced=`, reduced);
82
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,105 @@
1
+ /* Round 66 verification: pinnedStatus + pinnedGroup persist across page
2
+ * reloads via sessionStorage. Stale group pins (group no longer exists)
3
+ * are cleared on hydration so the chip row doesn't show a phantom team.
4
+ *
5
+ * - Pre-seed sessionStorage with anet-topo-pinned-status='working' →
6
+ * reload → assert filter pill renders + working stays bright + idles
7
+ * dim. (Status pin survives.)
8
+ * - Pre-seed anet-topo-pinned-group='alpha' WITH alpha sessions → reload
9
+ * → pill renders + alpha team stays bright.
10
+ * - Pre-seed anet-topo-pinned-group='ghost' WITHOUT any ghost session →
11
+ * reload → the stale pin is cleared on hydration; no pill renders.
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
+ async function probe({ status = null, group = null, sessions }) {
20
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(({ status, group }) => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', 'grid');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ if (status) sessionStorage.setItem('anet-topo-pinned-status', status);
28
+ else sessionStorage.removeItem('anet-topo-pinned-status');
29
+ if (group) sessionStorage.setItem('anet-topo-pinned-group', group);
30
+ else sessionStorage.removeItem('anet-topo-pinned-group');
31
+ } catch {}
32
+ }, { status, group });
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 seeded = sessions.map(s => ({
39
+ alias: s.alias, status: s.status, 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: seeded } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
48
+ await page.waitForFunction(c => document.querySelectorAll('g[data-node]').length === c, sessions.length, { timeout: 30000 });
49
+ await page.waitForTimeout(700);
50
+ const result = await page.evaluate(() => {
51
+ const pills = [...document.querySelectorAll('[data-active-filter]')].map(el => ({
52
+ kind: el.getAttribute('data-active-filter'),
53
+ text: (el.textContent || '').trim(),
54
+ }));
55
+ const opacities = {};
56
+ for (const el of document.querySelectorAll('g[data-node]')) {
57
+ const a = el.getAttribute('data-node');
58
+ if (a) opacities[a] = +(el.style.opacity || '1');
59
+ }
60
+ const storage = {
61
+ status: sessionStorage.getItem('anet-topo-pinned-status'),
62
+ group: sessionStorage.getItem('anet-topo-pinned-group'),
63
+ };
64
+ return { pills, opacities, storage };
65
+ });
66
+ await ctx.close();
67
+ return result;
68
+ }
69
+
70
+ const sessions3 = [
71
+ { alias: 'alpha1', status: 'working' },
72
+ { alias: 'alpha2', status: 'idle' },
73
+ { alias: 'beta', status: 'idle' },
74
+ ];
75
+ const alphaPlus = [
76
+ { alias: 'alpha1', status: 'idle' },
77
+ { alias: 'alpha2', status: 'idle' },
78
+ { alias: 'alpha3', status: 'idle' },
79
+ { alias: 'beta', status: 'idle' },
80
+ ];
81
+
82
+ const statusRestore = await probe({ status: 'working', sessions: sessions3 });
83
+ const groupRestore = await probe({ group: 'alpha', sessions: alphaPlus });
84
+ const stalePin = await probe({ group: 'ghost', sessions: sessions3 });
85
+
86
+ await browser.close();
87
+
88
+ const bright = (v) => v != null && v >= 0.55;
89
+ const dim = (v) => v != null && v < 0.4;
90
+ const results = {
91
+ statusPill_restored: statusRestore.pills.length === 1 && statusRestore.pills[0].kind === 'status'
92
+ && /filter:\s*working/.test(statusRestore.pills[0].text),
93
+ statusPin_dimsIdle: bright(statusRestore.opacities.alpha1) && dim(statusRestore.opacities.alpha2),
94
+ groupPill_restored: groupRestore.pills.length === 1 && groupRestore.pills[0].kind === 'group'
95
+ && /filter:\s*alpha/.test(groupRestore.pills[0].text),
96
+ groupPin_dimsOutsider: bright(groupRestore.opacities.alpha1) && dim(groupRestore.opacities.beta),
97
+ stale_pillCleared: stalePin.pills.length === 0,
98
+ stale_storageCleared: stalePin.storage.group === null,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} pin persistence:`, JSON.stringify(results),
102
+ `\n statusRestore=`, statusRestore,
103
+ `\n groupRestore=`, groupRestore,
104
+ `\n stalePin=`, stalePin);
105
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,78 @@
1
+ /* Round 37 verification: ChatPopover header carries cwd / last-seen
2
+ * lines pulled from the SWR session cache. Three cases:
3
+ * - online node with cwd → cwd line, no last-seen
4
+ * - offline node with cwd → both lines
5
+ * - bare node (no cwd, no last_seen) → drag/Esc fallback hint */
6
+ import { chromium } from 'playwright';
7
+ import { readFileSync } from 'node:fs';
8
+
9
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
10
+ const browser = await chromium.launch({ headless: true });
11
+
12
+ async function probe(targetAlias) {
13
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
14
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
15
+ await ctx.addInitScript(() => {
16
+ try {
17
+ localStorage.setItem('anet-theme', 'cyber');
18
+ localStorage.setItem('anet-topo-layout', 'grid');
19
+ localStorage.setItem('anet-topo-nodescale', '1');
20
+ sessionStorage.setItem('anet_v3_auth', '1');
21
+ } catch {}
22
+ });
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+ const offline = new Date(Date.now() - 8 * 60 * 1000).toISOString(); // 8 min ago
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 sessions = [
30
+ { alias: 'liveCwd', status: 'idle', network_id: nid,
31
+ project_dir: '/home/vansin/agent-orchestra',
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh },
33
+ { alias: 'offCwd', status: 'offline', network_id: nid,
34
+ project_dir: '/home/vansin/some-project',
35
+ created_at: fresh, updated_at: fresh, last_seen_at: offline },
36
+ { alias: 'bare', status: 'idle', network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh },
38
+ ];
39
+ await route.fulfill({ response: r, json: { ...b, sessions } });
40
+ });
41
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
44
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
45
+ await page.waitForTimeout(400);
46
+ await page.locator(`g[data-node="${targetAlias}"]`).click();
47
+ await page.waitForTimeout(300);
48
+ const meta = await page.evaluate(() => {
49
+ const cwd = document.querySelector('[data-popover-cwd]');
50
+ const ls = document.querySelector('[data-popover-lastseen]');
51
+ const popover = document.querySelector('[aria-label^="Chat with"]');
52
+ const dragHint = popover ? [...popover.querySelectorAll('div')].find(d => /Drag to move/.test(d.textContent || '')) : null;
53
+ return {
54
+ cwdText: cwd?.textContent || null,
55
+ lsText: ls?.textContent || null,
56
+ dragHintPresent: !!dragHint,
57
+ };
58
+ });
59
+ await ctx.close();
60
+ return meta;
61
+ }
62
+
63
+ const live = await probe('liveCwd');
64
+ const off = await probe('offCwd');
65
+ const bare = await probe('bare');
66
+ await browser.close();
67
+
68
+ const results = {
69
+ liveShowsCwd: live.cwdText === 'cwd: /home/vansin/agent-orchestra' && live.lsText === null && !live.dragHintPresent,
70
+ offlineShowsBoth: off.cwdText === 'cwd: /home/vansin/some-project'
71
+ && /last seen: [78]m ago/.test(off.lsText || '')
72
+ && !off.dragHintPresent,
73
+ bareFallsBackToDragHint: bare.cwdText === null && bare.lsText === null && bare.dragHintPresent,
74
+ };
75
+ const ok = Object.values(results).every(Boolean);
76
+ console.log(`${ok ? '✅' : '❌'} popover meta:`, JSON.stringify(results),
77
+ `\n live=`, live, `\n off=`, off, `\n bare=`, bare);
78
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,69 @@
1
+ /* Round 31 verification: fleet-pressure chip with proportional segments.
2
+ * 4 working + 6 idle + 2 offline = 12 total → 33% / 50% / 17%. */
3
+ import { chromium } from 'playwright';
4
+ import { readFileSync } from 'node:fs';
5
+
6
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
7
+ const browser = await chromium.launch({ headless: true });
8
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
9
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
10
+ await ctx.addInitScript(() => {
11
+ try {
12
+ localStorage.setItem('anet-theme', 'cyber');
13
+ localStorage.setItem('anet-topo-layout', 'grid');
14
+ sessionStorage.setItem('anet_v3_auth', '1');
15
+ } catch {}
16
+ });
17
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
18
+ await ctx.route('**/api/hub/status*', async (route) => {
19
+ const r = await route.fetch();
20
+ const b = await r.json();
21
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
22
+ const make = (alias, status) => ({
23
+ alias, status, network_id: nid, project_dir: null,
24
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
25
+ });
26
+ const sessions = [
27
+ ...Array.from({ length: 4 }, (_, i) => make(`w${i}`, 'working')),
28
+ ...Array.from({ length: 6 }, (_, i) => make(`i${i}`, 'idle')),
29
+ ...Array.from({ length: 2 }, (_, i) => make(`o${i}`, 'offline')),
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.waitForSelector('[data-fleet-pressure]', { timeout: 30000 });
37
+ await page.waitForTimeout(400);
38
+
39
+ const chip = '[data-fleet-pressure]';
40
+ const titleText = await page.locator(chip).getAttribute('title');
41
+ // The chip layout: <chip> > <label-span> + <bar-span> ; bar-span has the 3 segment spans.
42
+ const segments = await page.evaluate(() => {
43
+ const chip = document.querySelector('[data-fleet-pressure]');
44
+ if (!chip) return [];
45
+ // The bar container is the inline-flex w-16 span (second child).
46
+ const bar = [...chip.children].find(c => c.classList?.contains('w-16'));
47
+ if (!bar) return [];
48
+ return [...bar.children].map(el => ({ width: el.style.width, background: el.style.background }));
49
+ });
50
+ const segmentWidths = segments.map(s => s.width);
51
+ const segmentColors = segments.map(s => s.background);
52
+
53
+ // 4/12 = 33.33%, 6/12 = 50.00%, 2/12 = 16.67%
54
+ const close = (a, b) => Math.abs(parseFloat(a) - b) < 0.5;
55
+
56
+ await browser.close();
57
+ const results = {
58
+ titleHasCounts: titleText === '4 working · 6 idle · 2 offline',
59
+ threeSegments: segmentWidths.length === 3,
60
+ workingProportion: close(segmentWidths[0], 100 * 4 / 12),
61
+ idleProportion: close(segmentWidths[1], 100 * 6 / 12),
62
+ offlineProportion: close(segmentWidths[2], 100 * 2 / 12),
63
+ workingColor: /22c55e|rgb\(34, 197, 94\)/.test(segmentColors[0] || ''),
64
+ idleColor: /2dd4bf|rgb\(45, 212, 191\)/.test(segmentColors[1] || ''),
65
+ offlineColor: /6b7280|rgb\(107, 114, 128\)/.test(segmentColors[2] || ''),
66
+ };
67
+ const ok = Object.values(results).every(Boolean);
68
+ console.log(`${ok ? '✅' : '❌'} pressure bar:`, JSON.stringify(results), `widths=${segmentWidths.join(',')} colors=${segmentColors.join(' | ')}`);
69
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,123 @@
1
+ /* Round 83 verification: pressure-bar segments expose a hover preview
2
+ * before the click pins. Matches the R55 legend + R79 status chip +
3
+ * R80 vendor letter hover pattern — same activeStatus mechanism, just
4
+ * the last surface that was click-only.
5
+ *
6
+ * Fleet: 1 working + 2 idle + 1 offline. Hover the working segment →
7
+ * working node stays bright, idle + offline dim. Mouseleave → restore.
8
+ * Then click the working segment → pin sticks via boxShadow. Hovering
9
+ * idle while working is pinned → live preview switches; mouseleave
10
+ * falls back to the working pin (activeStatus = hoveredStatus ??
11
+ * pinnedStatus).
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: 1200 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ sessionStorage.setItem('anet_v3_auth', '1');
25
+ sessionStorage.removeItem('anet-topo-pinned-status');
26
+ sessionStorage.removeItem('anet-topo-pinned-group');
27
+ } catch {}
28
+ });
29
+
30
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
31
+ const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
32
+ await ctx.route('**/api/hub/status*', async (route) => {
33
+ const r = await route.fetch();
34
+ const b = await r.json();
35
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
36
+ const sessions = [
37
+ { alias: 'wkr', status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
38
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ { alias: 'idl1', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
40
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
41
+ { alias: 'idl2', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
42
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
43
+ { alias: 'off', status: 'offline', model: 'claude-opus-4', runtime: 'cli-claude-code',
44
+ network_id: nid, project_dir: null, created_at: stale10, updated_at: stale10, last_seen_at: stale10 },
45
+ ];
46
+ await route.fulfill({ response: r, json: { ...b, sessions } });
47
+ });
48
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
49
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
52
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
53
+ await page.waitForTimeout(500);
54
+
55
+ const ops = () => page.evaluate(() => {
56
+ const o = {};
57
+ for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
58
+ const g = document.querySelector(`g[data-node="${a}"]`);
59
+ o[a] = g ? +(g.style.opacity || '1') : null;
60
+ }
61
+ return o;
62
+ });
63
+ const segShadow = (k) => page.evaluate((key) => {
64
+ const s = document.querySelector(`[data-pressure-seg="${key}"]`);
65
+ return s ? (s.getAttribute('style') || '').includes('inset') : false;
66
+ }, k);
67
+
68
+ const before = await ops();
69
+
70
+ // Hover working segment → working stays bright, others dim.
71
+ await page.locator('[data-pressure-seg="working"]').hover();
72
+ await page.waitForTimeout(250);
73
+ const onHoverWorking = await ops();
74
+ const hoverPinShadowBefore = await segShadow('working');
75
+
76
+ // Mouseleave → baseline restores (no pin yet).
77
+ await page.mouse.move(10, 10);
78
+ await page.waitForTimeout(250);
79
+ const afterLeave = await ops();
80
+
81
+ // Click to pin working — segment gets inset shadow.
82
+ await page.locator('[data-pressure-seg="working"]').click();
83
+ await page.mouse.move(10, 10);
84
+ await page.waitForTimeout(250);
85
+ const afterPin = await ops();
86
+ const pinShadow = await segShadow('working');
87
+
88
+ // Hover idle while working is pinned — hoveredStatus overrides for the
89
+ // duration of the hover. idle stays bright, working dims (transient).
90
+ await page.locator('[data-pressure-seg="idle"]').hover();
91
+ await page.waitForTimeout(250);
92
+ const onHoverIdle = await ops();
93
+
94
+ // Mouseleave → falls back to working pin (NOT cleared).
95
+ await page.mouse.move(10, 10);
96
+ await page.waitForTimeout(250);
97
+ const afterLeaveIdle = await ops();
98
+
99
+ await browser.close();
100
+
101
+ const bright = (v) => v != null && v >= 0.55;
102
+ const dim = (v) => v != null && v < 0.4;
103
+ const results = {
104
+ before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2) && before.off <= 0.6,
105
+ hoverWorking_workerStays: bright(onHoverWorking.wkr),
106
+ hoverWorking_idlesDim: dim(onHoverWorking.idl1) && dim(onHoverWorking.idl2),
107
+ hoverWorking_offDim: dim(onHoverWorking.off),
108
+ hoverWorking_noPinYet: !hoverPinShadowBefore,
109
+ leave_restoresBaseline: bright(afterLeave.wkr) && bright(afterLeave.idl1) && bright(afterLeave.idl2),
110
+ pin_workerStays: bright(afterPin.wkr),
111
+ pin_idlesDim: dim(afterPin.idl1) && dim(afterPin.idl2),
112
+ pin_shadowRendered: pinShadow,
113
+ hoverIdle_transient: bright(onHoverIdle.idl1) && bright(onHoverIdle.idl2) && dim(onHoverIdle.wkr),
114
+ leaveIdle_pinPersists: bright(afterLeaveIdle.wkr) && dim(afterLeaveIdle.idl1) && dim(afterLeaveIdle.idl2),
115
+ };
116
+ const ok = Object.values(results).every(Boolean);
117
+ console.log(`${ok ? '✅' : '❌'} pressure-hover:`, JSON.stringify(results),
118
+ `\n before=`, before,
119
+ `\n onHoverWorking=`, onHoverWorking,
120
+ `\n afterPin=`, afterPin,
121
+ `\n onHoverIdle=`, onHoverIdle,
122
+ `\n afterLeaveIdle=`, afterLeaveIdle);
123
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,119 @@
1
+ /* Round 60 verification: each pressure-bar segment toggles a sticky
2
+ * status filter. Click "working" → all non-working nodes dim, working
3
+ * ones stay. Click again → release. Pin and the R55 legend hover feed
4
+ * the same node-opacity branch via `activeStatus = hoveredStatus ??
5
+ * pinnedStatus`, so hover transiently overrides without nuking the pin.
6
+ *
7
+ * Sessions:
8
+ * wkr — working
9
+ * idl1 — idle
10
+ * idl2 — idle
11
+ * off — offline
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 {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ const stale = new Date(Date.now() - 30 * 60 * 1000).toISOString();
31
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const sessions = [
36
+ { alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
37
+ { alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
38
+ { alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ { alias: 'off', status: 'offline', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
40
+ ];
41
+ await route.fulfill({ response: r, json: { ...b, sessions } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
47
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
48
+ await page.waitForTimeout(500);
49
+
50
+ const opacities = () => page.evaluate(() => {
51
+ const o = {};
52
+ for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
53
+ const g = document.querySelector(`g[data-node="${a}"]`);
54
+ o[a] = g ? +(g.style.opacity || '1') : null;
55
+ }
56
+ return o;
57
+ });
58
+
59
+ const before = await opacities();
60
+
61
+ const bright = (v) => v != null && v >= 0.55;
62
+ const dim = (v) => v != null && v < 0.4;
63
+
64
+ // Click the WORKING segment.
65
+ const workingSeg = page.locator('[data-pressure-seg="working"]').first();
66
+ const workingExists = await workingSeg.count() > 0;
67
+ if (!workingExists) { console.log('❌ pressure segment[working] not found'); process.exit(1); }
68
+ await workingSeg.click({ force: true });
69
+ await page.waitForTimeout(250);
70
+ const afterPin = await opacities();
71
+
72
+ // Pin should persist past mouse moving away.
73
+ await page.mouse.move(10, 10);
74
+ await page.waitForTimeout(250);
75
+ const afterMouseMove = await opacities();
76
+
77
+ // Click again to release. R79 added hover-affordances on the working
78
+ // + online chips, so the click locator's mouse path can light a
79
+ // transient hoveredStatus en route to the pressure segment. Mouse-move
80
+ // to a neutral spot after the release click so we observe pin-only
81
+ // state (same gotcha R61's test documented inline).
82
+ await workingSeg.click({ force: true });
83
+ await page.mouse.move(10, 10);
84
+ await page.waitForTimeout(250);
85
+ const afterRelease = await opacities();
86
+
87
+ // Repeat for offline segment.
88
+ const offlineSeg = page.locator('[data-pressure-seg="offline"]').first();
89
+ await offlineSeg.click({ force: true });
90
+ await page.mouse.move(10, 10);
91
+ await page.waitForTimeout(250);
92
+ const afterOfflinePin = await opacities();
93
+ await offlineSeg.click({ force: true });
94
+ await page.mouse.move(10, 10);
95
+ await page.waitForTimeout(250);
96
+ const afterOfflineRelease = await opacities();
97
+
98
+ await browser.close();
99
+
100
+ const results = {
101
+ before_baseline: bright(before.wkr) && bright(before.idl1),
102
+ working_pinKeepsWorking: bright(afterPin.wkr),
103
+ working_pinDimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
104
+ working_pinDimsOffline: dim(afterPin.off),
105
+ working_pinSurvivesMouse: bright(afterMouseMove.wkr) && dim(afterMouseMove.idl1),
106
+ working_releaseRestoresAll: bright(afterRelease.wkr) && bright(afterRelease.idl1),
107
+ offline_pinKeepsOffline: bright(afterOfflinePin.off), // base offline 0.6 reads as bright (>=0.55)
108
+ offline_pinDimsOnline: dim(afterOfflinePin.wkr) && dim(afterOfflinePin.idl1),
109
+ offline_releaseRestoresAll: bright(afterOfflineRelease.wkr),
110
+ };
111
+ const ok = Object.values(results).every(Boolean);
112
+ console.log(`${ok ? '✅' : '❌'} pressure pin:`, JSON.stringify(results),
113
+ `\n before=`, before,
114
+ `\n afterPin=`, afterPin,
115
+ `\n afterMouseMove=`, afterMouseMove,
116
+ `\n afterRelease=`, afterRelease,
117
+ `\n afterOfflinePin=`, afterOfflinePin,
118
+ `\n afterOfflineRelease=`, afterOfflineRelease);
119
+ process.exit(ok ? 0 : 1);