@sleep2agi/agent-network-dashboard 0.4.6 → 0.5.0

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 (493) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +31 -31
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +16 -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/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
  37. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
  38. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
  41. package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
  42. package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
  43. package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
  44. package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
  45. package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
  46. package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
  47. package/.next/server/app/index.html +2 -2
  48. package/.next/server/app/index.rsc +14 -14
  49. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  50. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/login.html +2 -2
  56. package/.next/server/app/login.rsc +14 -14
  57. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  58. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  59. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  60. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  61. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  62. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  63. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app/logs.html +2 -2
  65. package/.next/server/app/logs.rsc +14 -14
  66. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  67. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  68. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  69. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  70. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  71. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  72. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  73. package/.next/server/app/messages.html +2 -2
  74. package/.next/server/app/messages.rsc +14 -14
  75. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  76. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  77. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  78. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  80. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  81. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/node.html +2 -2
  83. package/.next/server/app/node.rsc +14 -14
  84. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  85. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  86. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  87. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  88. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  89. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  90. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  91. package/.next/server/app/nodes.html +2 -2
  92. package/.next/server/app/nodes.rsc +14 -14
  93. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  94. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  95. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  96. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  97. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  98. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  99. package/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  101. package/.next/server/app/server-logs.html +2 -2
  102. package/.next/server/app/server-logs.rsc +14 -14
  103. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  104. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  105. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  106. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  107. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  108. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  109. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  110. package/.next/server/app/settings/networks.html +2 -2
  111. package/.next/server/app/settings/networks.rsc +14 -14
  112. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  113. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  114. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  115. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  116. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  117. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  118. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  119. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  121. package/.next/server/app/settings/tokens.html +2 -2
  122. package/.next/server/app/settings/tokens.rsc +14 -14
  123. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  124. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  125. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  126. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  127. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  128. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  129. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  130. package/.next/server/app/settings.html +2 -2
  131. package/.next/server/app/settings.rsc +14 -14
  132. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  133. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  134. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  135. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  136. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  137. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  138. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  139. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  140. package/.next/server/app/tasks.html +2 -2
  141. package/.next/server/app/tasks.rsc +14 -14
  142. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  143. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  144. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  145. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  146. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  147. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  148. package/.next/server/app-paths-manifest.json +2 -0
  149. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
  150. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
  151. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
  152. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
  153. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
  154. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
  155. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
  156. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
  157. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
  158. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -2
  160. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  167. package/.next/server/middleware-build-manifest.js +3 -3
  168. package/.next/server/pages/404.html +2 -2
  169. package/.next/server/pages/500.html +1 -1
  170. package/.next/server/server-reference-manifest.js +1 -1
  171. package/.next/server/server-reference-manifest.json +1 -1
  172. package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
  173. package/.next/static/chunks/0f~0kgy2pycyc.js +1 -0
  174. package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
  175. package/.next/static/chunks/1175-dp18a1e_.js +1 -0
  176. package/.next/static/chunks/1552j0415oeal.js +4 -0
  177. package/.next/trace +2 -2
  178. package/.next/trace-build +1 -1
  179. package/.next/types/routes.d.ts +3 -1
  180. package/.next/types/validator.ts +18 -0
  181. package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
  182. package/app/api/hub/server/[hostname]/health/route.ts +58 -0
  183. package/app/api/hub/servers/route.ts +88 -1
  184. package/app/components/CommandPalette.tsx +107 -0
  185. package/app/components/HelpOverlay.tsx +109 -1
  186. package/app/components/ServersDrawer.tsx +181 -5
  187. package/app/components/TopoGraph.tsx +6409 -286
  188. package/app/globals.css +110 -5
  189. package/app/lib/vendorIdentity.ts +14 -3
  190. package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
  191. package/package.json +2 -2
  192. package/public/sleep2agi-logo.svg +11 -0
  193. package/public/vendors/claude.svg +11 -0
  194. package/public/vendors/minimax.svg +11 -0
  195. package/public/vendors/openai.svg +12 -0
  196. package/screenshots/r293-p0/after-full.png +0 -0
  197. package/screenshots/r293-p0/after-topo.png +0 -0
  198. package/screenshots/r293-p0/baseline-full.png +0 -0
  199. package/screenshots/r293-p0/baseline-topo.png +0 -0
  200. package/screenshots/r293-p0/logo-integrated-full.png +0 -0
  201. package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
  202. package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
  203. package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
  204. package/screenshots/r293-p0/wave1-after-full.png +0 -0
  205. package/screenshots/r293-p0/wave1-after-topo.png +0 -0
  206. package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
  207. package/scripts/p0-topo-screenshot.mjs +82 -0
  208. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  209. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  210. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  211. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  212. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  213. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  214. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  215. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  216. package/scripts/topo-brand-logo-transition-test.mjs +94 -0
  217. package/scripts/topo-brand-watermark-test.mjs +97 -0
  218. package/scripts/topo-canvas-aria-test.mjs +108 -0
  219. package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
  220. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  221. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  222. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  223. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  224. package/scripts/topo-chip-focus-test.mjs +95 -0
  225. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  226. package/scripts/topo-chip-row-gap-test.mjs +90 -0
  227. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  228. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  229. package/scripts/topo-chip-unit-demotion-test.mjs +93 -0
  230. package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
  231. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  232. package/scripts/topo-chrome-active-weight-test.mjs +104 -0
  233. package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
  234. package/scripts/topo-chrome-gap-test.mjs +73 -0
  235. package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
  236. package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
  237. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  238. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  239. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  240. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  241. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  242. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  243. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  244. package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
  245. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  246. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  247. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  248. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  249. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  250. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  251. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  252. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  253. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  254. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  255. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  256. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  257. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  258. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  259. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  260. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  261. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  262. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  263. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  264. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  265. package/scripts/topo-edge-fadein-test.mjs +106 -0
  266. package/scripts/topo-edge-focus-test.mjs +113 -0
  267. package/scripts/topo-edge-hover-test.mjs +13 -5
  268. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  269. package/scripts/topo-edge-pill-test.mjs +105 -0
  270. package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
  271. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  272. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  273. package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
  274. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  275. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  276. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  277. package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
  278. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  279. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  280. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  281. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  282. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  283. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  284. package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
  285. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  286. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  287. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  288. package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
  289. package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
  290. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  291. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  292. package/scripts/topo-group-count-test.mjs +6 -1
  293. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  294. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  295. package/scripts/topo-group-label-hover-test.mjs +119 -0
  296. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  297. package/scripts/topo-group-label-tint-test.mjs +99 -0
  298. package/scripts/topo-group-label-title-test.mjs +83 -0
  299. package/scripts/topo-group-pin-test.mjs +119 -0
  300. package/scripts/topo-group-pip-fadein-test.mjs +104 -0
  301. package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
  302. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  303. package/scripts/topo-group-status-pips-test.mjs +103 -0
  304. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  305. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  306. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  307. package/scripts/topo-groupbox-live-test.mjs +100 -0
  308. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  309. package/scripts/topo-groupbox-test.mjs +4 -2
  310. package/scripts/topo-header-gap-test.mjs +94 -0
  311. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  312. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  313. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  314. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  315. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  316. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  317. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  318. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  319. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  320. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  321. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  322. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  323. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  324. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  325. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  326. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  327. package/scripts/topo-hub-a11y-test.mjs +112 -0
  328. package/scripts/topo-hub-busyness-test.mjs +100 -0
  329. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  330. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  331. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  332. package/scripts/topo-hub-fadein-test.mjs +88 -0
  333. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  334. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  335. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  336. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  337. package/scripts/topo-hub-title-test.mjs +7 -2
  338. package/scripts/topo-hub-working-count-test.mjs +99 -0
  339. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  340. package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
  341. package/scripts/topo-kicker-color-test.mjs +93 -0
  342. package/scripts/topo-kicker-tracking-test.mjs +101 -0
  343. package/scripts/topo-kicker-weight-test.mjs +98 -0
  344. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  345. package/scripts/topo-label-lift-test.mjs +5 -1
  346. package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
  347. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  348. package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
  349. package/scripts/topo-layout-toggle-test.mjs +131 -0
  350. package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
  351. package/scripts/topo-legend-compress-test.mjs +107 -0
  352. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  353. package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
  354. package/scripts/topo-legend-count-tabular-test.mjs +87 -0
  355. package/scripts/topo-legend-count-test.mjs +88 -0
  356. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  357. package/scripts/topo-legend-count-weight-test.mjs +86 -0
  358. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  359. package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
  360. package/scripts/topo-legend-hover-test.mjs +113 -0
  361. package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
  362. package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
  363. package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
  364. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  365. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  366. package/scripts/topo-legend-pin-test.mjs +128 -0
  367. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  368. package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
  369. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  370. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  371. package/scripts/topo-legend-row-title-test.mjs +102 -0
  372. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  373. package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
  374. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  375. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  376. package/scripts/topo-minimap-radius-test.mjs +88 -0
  377. package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
  378. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  379. package/scripts/topo-monogram-font-test.mjs +112 -0
  380. package/scripts/topo-monogram-stroke-test.mjs +101 -0
  381. package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
  382. package/scripts/topo-node-a11y-test.mjs +107 -0
  383. package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
  384. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  385. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  386. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  387. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  388. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  389. package/scripts/topo-node-title-peers-test.mjs +107 -0
  390. package/scripts/topo-node-title-test.mjs +101 -0
  391. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  392. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  393. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  394. package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
  395. package/scripts/topo-panel-count-unit-test.mjs +112 -0
  396. package/scripts/topo-panel-elevation-test.mjs +76 -0
  397. package/scripts/topo-panel-fadein-test.mjs +102 -0
  398. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  399. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  400. package/scripts/topo-panel-rx-test.mjs +90 -0
  401. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  402. package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
  403. package/scripts/topo-panel-title-tracking-test.mjs +78 -0
  404. package/scripts/topo-particle-stagger-test.mjs +82 -0
  405. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  406. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  407. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  408. package/scripts/topo-pin-intersection-test.mjs +121 -0
  409. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  410. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  411. package/scripts/topo-pin-persistence-test.mjs +105 -0
  412. package/scripts/topo-prefix-opacity-test.mjs +107 -0
  413. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  414. package/scripts/topo-pressure-hover-test.mjs +123 -0
  415. package/scripts/topo-pressure-pin-test.mjs +119 -0
  416. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  417. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  418. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  419. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  420. package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
  421. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  422. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  423. package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
  424. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  425. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  426. package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
  427. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  428. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  429. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  430. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  431. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  432. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  433. package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
  434. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  435. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  436. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  437. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  438. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  439. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  440. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  441. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  442. package/scripts/topo-recent-row-title-test.mjs +112 -0
  443. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  444. package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
  445. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  446. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  447. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  448. package/scripts/topo-reset-spin-test.mjs +120 -0
  449. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  450. package/scripts/topo-spokes-retired-test.mjs +93 -0
  451. package/scripts/topo-starfield-density-test.mjs +89 -0
  452. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  453. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  454. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  455. package/scripts/topo-svg-focus-test.mjs +112 -0
  456. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  457. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  458. package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
  459. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  460. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  461. package/scripts/topo-tier-ring-test.mjs +69 -0
  462. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  463. package/scripts/topo-tier-stagger-test.mjs +89 -0
  464. package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
  465. package/scripts/topo-title-tracking-test.mjs +98 -0
  466. package/scripts/topo-titleblock-gap-test.mjs +86 -0
  467. package/scripts/topo-titleblock-margin-test.mjs +83 -0
  468. package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
  469. package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
  470. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  471. package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
  472. package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
  473. package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
  474. package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
  475. package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
  476. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  477. package/scripts/topo-vendor-pill-test.mjs +125 -0
  478. package/scripts/topo-vendor-pin-test.mjs +125 -0
  479. package/scripts/topo-view-smooth-test.mjs +131 -0
  480. package/scripts/topo-watermark-tracking-test.mjs +99 -0
  481. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  482. package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
  483. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  484. package/scripts/topo-wrapper-radius-test.mjs +73 -0
  485. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  486. package/.next/static/chunks/088ouqy7tekty.js +0 -2
  487. package/.next/static/chunks/0hyl7mdapsz38.js +0 -1
  488. package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
  489. package/.next/static/chunks/127kw7~wsnyu7.css +0 -2
  490. package/.next/static/chunks/16yfxp9ak2gdm.js +0 -1
  491. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
  492. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
  493. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
@@ -0,0 +1,133 @@
1
+ /* Round 90 verification: Cmd+K palette "Clear topology filters" also
2
+ * releases the R88 pinnedVendor (the R69 implementation only knew
3
+ * about status + group). Plus the new granular "Clear topology vendor
4
+ * filter" command releases just vendor without disturbing status or
5
+ * group pins.
6
+ *
7
+ * Drive via direct CustomEvent dispatch (avoids fuzzy-match flake —
8
+ * same approach as the R82 chip-pin-mirror test). Verifies both the
9
+ * TopoGraph listener branch (state mutation) and the in-memory
10
+ * sessionStorage cleanup.
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try {
21
+ localStorage.setItem('anet-theme', 'cyber');
22
+ localStorage.setItem('anet-topo-layout', 'ring');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ sessionStorage.removeItem('anet-topo-pinned-status');
25
+ sessionStorage.removeItem('anet-topo-pinned-group');
26
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
27
+ } catch {}
28
+ });
29
+
30
+ const fresh = new Date(Date.now() - 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 mk = (alias, status, model) => ({
36
+ alias, status, model, runtime: 'cli-claude-code',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ const sessions = [
41
+ mk('alpha', 'working', 'claude-opus-4'),
42
+ mk('beta', 'idle', 'gpt-5'),
43
+ mk('gamma', 'idle', 'claude-opus-4'),
44
+ ];
45
+ await route.fulfill({ response: r, json: { ...b, sessions } });
46
+ });
47
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
48
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const pills = () => page.evaluate(() => {
55
+ const all = document.querySelectorAll('[data-active-filter]');
56
+ return [...all].map(p => p.getAttribute('data-active-filter'));
57
+ });
58
+ const storage = () => page.evaluate(() => ({
59
+ status: sessionStorage.getItem('anet-topo-pinned-status'),
60
+ group: sessionStorage.getItem('anet-topo-pinned-group'),
61
+ vendor: sessionStorage.getItem('anet-topo-pinned-vendor'),
62
+ }));
63
+
64
+ // Helper: dispatch a topo-pin event from the page side.
65
+ const pin = async (kind, value) => {
66
+ await page.evaluate(({ kind, value }) => {
67
+ if (kind === 'status') sessionStorage.setItem('anet-topo-pinned-status', value);
68
+ if (kind === 'group') sessionStorage.setItem('anet-topo-pinned-group', value);
69
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind, value } }));
70
+ }, { kind, value });
71
+ await page.waitForTimeout(200);
72
+ };
73
+ // Pin vendor via the actual UI letter click (no CustomEvent for that
74
+ // flow yet — palette doesn't surface per-vendor pin actions).
75
+ const pinVendor = async (initial) => {
76
+ await page.locator(`[data-vendor-letter="${initial}"]`).click();
77
+ await page.mouse.move(10, 10);
78
+ await page.waitForTimeout(200);
79
+ };
80
+ const clearAll = async () => {
81
+ await page.evaluate(() => {
82
+ sessionStorage.removeItem('anet-topo-pinned-status');
83
+ sessionStorage.removeItem('anet-topo-pinned-group');
84
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
85
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear' } }));
86
+ });
87
+ await page.waitForTimeout(250);
88
+ };
89
+ const clearVendor = async () => {
90
+ await page.evaluate(() => {
91
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
92
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear-vendor' } }));
93
+ });
94
+ await page.waitForTimeout(250);
95
+ };
96
+
97
+ // === Test 1: clear-all now also clears vendor (the R90 bug fix). ===
98
+ await pin('status', 'working');
99
+ await pinVendor('A');
100
+ const afterPinBoth = { pills: await pills(), storage: await storage() };
101
+
102
+ await clearAll();
103
+ const afterClearAll = { pills: await pills(), storage: await storage() };
104
+
105
+ // === Test 2: granular clear-vendor leaves status + group untouched. ===
106
+ await pin('status', 'working');
107
+ await pinVendor('A');
108
+ const beforeGranular = { pills: await pills(), storage: await storage() };
109
+
110
+ await clearVendor();
111
+ const afterClearVendor = { pills: await pills(), storage: await storage() };
112
+
113
+ await browser.close();
114
+
115
+ const sameSet = (a, b) => a.length === b.length && a.every(x => b.includes(x));
116
+ const results = {
117
+ pinBoth_threePills: sameSet(afterPinBoth.pills, ['status', 'vendor']),
118
+ pinBoth_storageBoth: afterPinBoth.storage.status === 'working' && afterPinBoth.storage.vendor === 'A',
119
+ clearAll_noPills: afterClearAll.pills.length === 0,
120
+ clearAll_storageAllNull: afterClearAll.storage.status === null && afterClearAll.storage.group === null && afterClearAll.storage.vendor === null,
121
+ granular_threePills: sameSet(beforeGranular.pills, ['status', 'vendor']),
122
+ clearVendor_keepsStatus: afterClearVendor.pills.includes('status'),
123
+ clearVendor_dropsVendor: !afterClearVendor.pills.includes('vendor'),
124
+ clearVendor_storageStatusKept: afterClearVendor.storage.status === 'working',
125
+ clearVendor_storageVendorCleared: afterClearVendor.storage.vendor === null,
126
+ };
127
+ const ok = Object.values(results).every(Boolean);
128
+ console.log(`${ok ? '✅' : '❌'} cmdk vendor clear:`, JSON.stringify(results),
129
+ `\n afterPinBoth=`, afterPinBoth,
130
+ `\n afterClearAll=`, afterClearAll,
131
+ `\n beforeGranular=`, beforeGranular,
132
+ `\n afterClearVendor=`, afterClearVendor);
133
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 108 verification: Cmd+K palette can pin a vendor filter via
2
+ * three new static actions ("Pin topology filter: Anthropic" /
3
+ * OpenAI / 书生). TopoGraph's R69 listener handles kind='vendor' to
4
+ * setPinnedVendor. The R88 stale-purge cleans up if the vendor isn't
5
+ * in the fleet.
6
+ *
7
+ * Drives via direct CustomEvent dispatch (same deterministic path
8
+ * R82/R90 tests use — fuzzy-match-free). Verifies pin sticks, vendor
9
+ * pill renders, storage persists, and Esc / clear-all release.
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const browser = await chromium.launch({ headless: true });
16
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
17
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
18
+ await ctx.addInitScript(() => {
19
+ try {
20
+ localStorage.setItem('anet-theme', 'cyber');
21
+ localStorage.setItem('anet-topo-layout', 'ring');
22
+ sessionStorage.setItem('anet_v3_auth', '1');
23
+ sessionStorage.removeItem('anet-topo-pinned-status');
24
+ sessionStorage.removeItem('anet-topo-pinned-group');
25
+ sessionStorage.removeItem('anet-topo-pinned-vendor');
26
+ } catch {}
27
+ });
28
+
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const mk = (alias, model) => ({
35
+ alias, status: 'idle', model, runtime: 'cli-claude-code',
36
+ 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
+ mk('alpha', 'claude-opus-4'), // A (Anthropic)
41
+ mk('beta', 'gpt-5'), // O (OpenAI)
42
+ mk('gamma', 'claude-opus-4'), // A
43
+ ] } });
44
+ });
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
46
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
50
+ await page.waitForTimeout(400);
51
+
52
+ const pillCount = () => page.locator('[data-active-filter="vendor"]').count();
53
+ const storage = () => page.evaluate(() => sessionStorage.getItem('anet-topo-pinned-vendor'));
54
+ const opacities = () => page.evaluate(() => {
55
+ const o = {};
56
+ for (const a of ['alpha', 'beta', 'gamma']) {
57
+ const g = document.querySelector(`g[data-node="${a}"]`);
58
+ o[a] = g ? +(g.style.opacity || '1') : null;
59
+ }
60
+ return o;
61
+ });
62
+
63
+ // Initial state — no pin.
64
+ const beforePill = await pillCount();
65
+ const beforeStorage = await storage();
66
+
67
+ // Pin A via R108 palette action's CustomEvent.
68
+ await page.evaluate(() => {
69
+ sessionStorage.setItem('anet-topo-pinned-vendor', 'A');
70
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'vendor', value: 'A' } }));
71
+ });
72
+ await page.waitForTimeout(300);
73
+ const afterPinA_ops = await opacities();
74
+ const afterPinA_pill = await pillCount();
75
+ const afterPinA_storage = await storage();
76
+
77
+ // Esc → clear pin.
78
+ await page.keyboard.press('Escape');
79
+ await page.waitForTimeout(300);
80
+ const afterEsc_pill = await pillCount();
81
+ const afterEsc_storage = await storage();
82
+
83
+ await browser.close();
84
+
85
+ const bright = (v) => v != null && v >= 0.55;
86
+ const dim = (v) => v != null && v < 0.4;
87
+ const results = {
88
+ before_noPill: beforePill === 0,
89
+ before_noStorage: beforeStorage === null,
90
+ pinA_pillShown: afterPinA_pill === 1,
91
+ pinA_storageWritten: afterPinA_storage === 'A',
92
+ pinA_keepsClaudes: bright(afterPinA_ops.alpha) && bright(afterPinA_ops.gamma),
93
+ pinA_dimsGPT: dim(afterPinA_ops.beta),
94
+ esc_clearsPill: afterEsc_pill === 0,
95
+ esc_clearsStorage: afterEsc_storage === null,
96
+ };
97
+ const ok = Object.values(results).every(Boolean);
98
+ console.log(`${ok ? '✅' : '❌'} cmdk vendor pin:`, JSON.stringify(results),
99
+ `\n afterPinA_ops=`, afterPinA_ops,
100
+ `\n afterPinA_pill=`, afterPinA_pill,
101
+ `\n afterPinA_storage=`, afterPinA_storage,
102
+ `\n afterEsc_pill=`, afterEsc_pill);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 262 verification: corner-radius vocabulary unified across the
2
+ * canvas chrome — panels drop rx="10" → rx="8" to match the wrapper's
3
+ * rounded-lg (8px).
4
+ *
5
+ * Pre-R262 corner-radius hierarchy:
6
+ * Group label rect: rx="4" ← tiny chip
7
+ * Node label card: rx="6" ← small card
8
+ * Recent-signal panel: rx="10" ← panel chrome ← MISMATCHED
9
+ * Legend panel: rx="10" ← panel chrome ← MISMATCHED
10
+ * Canvas wrapper: rounded-lg (8 px CSS)
11
+ * ⇒ Panels at 10 px more rounded than the wrapper at 8 px.
12
+ *
13
+ * Post-R262:
14
+ * Group label: rx=4
15
+ * Node label card:rx=6
16
+ * Panels: rx=8 ← matches wrapper
17
+ * Canvas wrapper: rounded-lg (8 px)
18
+ * ⇒ Clean 4→6→8 progression. Panel corners now flush with wrapper
19
+ * corners, not exceeding them.
20
+ *
21
+ * Test scope:
22
+ * 1. Recent-signal panel rect rx === "8".
23
+ * 2. Legend panel rect rx === "8".
24
+ * 3. Node label card rect rx === "6" (unchanged regression).
25
+ * 4. Group label rect rx === "4" (unchanged regression).
26
+ * 5. Canvas wrapper computed border-top-left-radius === 8 px.
27
+ */
28
+ import { chromium } from 'playwright';
29
+ import { readFileSync } from 'node:fs';
30
+
31
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
32
+ const browser = await chromium.launch({ headless: true });
33
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
34
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
35
+ await ctx.addInitScript(() => {
36
+ try {
37
+ localStorage.setItem('anet-theme', 'cyber');
38
+ sessionStorage.setItem('anet_v3_auth', '1');
39
+ // Grid layout exposes the group label rect (rx=4)
40
+ localStorage.setItem('anet-topo-layout', 'grid');
41
+ } catch {}
42
+ });
43
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
44
+ await ctx.route('**/api/hub/status*', async (route) => {
45
+ const r = await route.fetch();
46
+ const b = await r.json();
47
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
48
+ const mk = (alias) => ({
49
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
50
+ network_id: nid, project_dir: null,
51
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
52
+ });
53
+ // Use a group prefix so the prefix-group boundary box renders + its
54
+ // group label rect with rx=4 is in the DOM for regression check.
55
+ await route.fulfill({ response: r, json: { ...b, sessions: [
56
+ mk('alpha-1'), mk('alpha-2'), mk('alpha-3'), mk('alpha-4'),
57
+ ] } });
58
+ });
59
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
60
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
61
+
62
+ const page = await ctx.newPage();
63
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
64
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
65
+ await page.waitForSelector('[data-topo-panel="recent"]', { timeout: 10000 });
66
+ await page.waitForSelector('[data-topo-panel="legend"]', { timeout: 10000 });
67
+ await page.waitForSelector('[data-node-label-card]', { timeout: 10000 });
68
+ await page.waitForSelector('[data-group-label-tinted]', { timeout: 10000 });
69
+ await page.waitForSelector('[data-topo-wrapper]', { timeout: 10000 });
70
+ await page.waitForTimeout(300);
71
+
72
+ const probe = await page.evaluate(() => {
73
+ const recentG = document.querySelector('[data-topo-panel="recent"]');
74
+ const legendG = document.querySelector('[data-topo-panel="legend"]');
75
+ const recentRx = recentG?.querySelector('rect')?.getAttribute('rx');
76
+ const legendRx = legendG?.querySelector('rect')?.getAttribute('rx');
77
+ // Node label card rect — query any one of them
78
+ const cardRect = document.querySelector('[data-node-label-card]');
79
+ const cardRx = cardRect?.getAttribute('rx');
80
+ // Group label rect — the bg tint rect inside the group-label-hit group
81
+ const groupTintRect = document.querySelector('[data-group-label-tinted]');
82
+ const groupTintRx = groupTintRect?.getAttribute('rx');
83
+ // Wrapper CSS corner radius
84
+ const wrapper = document.querySelector('[data-topo-wrapper]');
85
+ const wrapperBorderRadius = wrapper ? window.getComputedStyle(wrapper).borderTopLeftRadius : null;
86
+ return {
87
+ recentRx, legendRx, cardRx, groupTintRx, wrapperBorderRadius,
88
+ };
89
+ });
90
+ await browser.close();
91
+
92
+ const results = {
93
+ recent_panel_rx_8: probe.recentRx === '8',
94
+ legend_panel_rx_8: probe.legendRx === '8',
95
+ node_label_card_rx_6: probe.cardRx === '6',
96
+ group_label_tint_rx_4: probe.groupTintRx === '4',
97
+ wrapper_border_radius_8px: probe.wrapperBorderRadius === '8px',
98
+ };
99
+ const ok = Object.values(results).every(Boolean);
100
+ console.log(`${ok ? '✅' : '❌'} corner-radius unify:`, JSON.stringify(results),
101
+ '\n rxs: recent', probe.recentRx, '· legend', probe.legendRx, '· card', probe.cardRx, '· group-tint', probe.groupTintRx,
102
+ '\n wrapper border-radius:', probe.wrapperBorderRadius);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,133 @@
1
+ /* Round 212 verification: dense plain-text alias (used when
2
+ * denseLayout > 16 nodes) gains fill transition on status flip,
3
+ * extending R211's card-mode coverage to the dense fallback path
4
+ * users see most on busy fleets.
5
+ *
6
+ * Pre-R212 the dense alias snap-cut its fill on tier change while
7
+ * the status ring smoothly transitioned (R167). R212 inline
8
+ * transition combines R26 transform 200ms (hover lift) with R212
9
+ * fill 300ms (status flip ease).
10
+ *
11
+ * Test (cyber dark, 17 sessions in mixed status → denseLayout=true):
12
+ * 1. 17+ [data-node-dense-alias-text] elements present (one per
13
+ * session — confirms denseLayout path fired, not card mode).
14
+ * 2. Inline transition includes BOTH 'transform 200ms' AND
15
+ * 'fill 300ms' (composite spliced, group-hover lift unbroken).
16
+ * 3. group-hover:-translate-y-[1.5px] className preserved (lift
17
+ * still wires via CSS pseudo-class).
18
+ * 4. Mixed-status fleet renders different fills per tier (working
19
+ * green ≠ idle teal ≠ offline gray family).
20
+ * 5. paintOrder: stroke + stroke attribute preserved (R110 halo).
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
+ // Build 17 sessions: mostly working, a couple idle + offline so fill
36
+ // colors visibly differ.
37
+ const statuses = [
38
+ ...Array(13).fill('working'),
39
+ 'working', 'idle', 'idle', 'offline',
40
+ ];
41
+ const aliases = statuses.map((_, i) => `node-${(i + 1).toString().padStart(2, '0')}`);
42
+
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
+ await route.fulfill({ response: r, json: { ...b, sessions: aliases.map((alias, i) => ({
48
+ alias, status: statuses[i], model: 'claude-opus-4', runtime: 'claude-code-cli',
49
+ network_id: nid, project_dir: null,
50
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
51
+ })) } });
52
+ });
53
+ await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
54
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
55
+
56
+ const page = await ctx.newPage();
57
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
58
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 17, { timeout: 30000 });
59
+ await page.waitForSelector('[data-node-dense-alias-text]', { timeout: 10000, state: 'attached' });
60
+ await page.waitForTimeout(300);
61
+
62
+ const probe = await page.evaluate(() => {
63
+ return [...document.querySelectorAll('[data-node-dense-alias-text]')].map(el => ({
64
+ alias: el.getAttribute('data-node-dense-alias-text'),
65
+ text: el.textContent,
66
+ fill: el.getAttribute('fill'),
67
+ className: el.getAttribute('class') || '',
68
+ transition: el.style.transition,
69
+ paintOrder: el.style.paintOrder,
70
+ stroke: el.getAttribute('stroke'),
71
+ }));
72
+ });
73
+
74
+ await browser.close();
75
+
76
+ const hasTransformT = (s) => /transform\s+(?:200ms|0\.2s)/.test(s || '');
77
+ const hasFillT = (s) => /fill\s+(?:300ms|0\.3s)/.test(s || '');
78
+
79
+ const workingAlias = probe.find(p => p.alias === 'node-01'); // first working
80
+ const idleAlias = probe.find(p => p.alias === 'node-15'); // first idle
81
+ const offlineAlias = probe.find(p => p.alias === 'node-17'); // offline
82
+
83
+ const results = {
84
+ dense_mode_fired: probe.length >= 17,
85
+ // R211 [data-node-alias-text] should NOT be present (card path skipped)
86
+ card_path_skipped: true, // implicit — if dense path fired, card path didn't render its texts
87
+ all_have_transition_fill: probe.every(p => hasFillT(p.transition)),
88
+ all_have_transition_trans: probe.every(p => hasTransformT(p.transition)),
89
+ all_keep_group_hover: probe.every(p => /group-hover:-translate-y/.test(p.className)),
90
+ all_have_paint_order: probe.every(p => p.paintOrder === 'stroke'),
91
+ all_have_halo_stroke: probe.every(p => !!p.stroke && p.stroke !== 'none'),
92
+ working_idle_fill_differs: workingAlias?.fill && idleAlias?.fill
93
+ && workingAlias.fill !== idleAlias.fill,
94
+ working_offline_fill_differs: workingAlias?.fill && offlineAlias?.fill
95
+ && workingAlias.fill !== offlineAlias.fill,
96
+ alias_text_rendered: workingAlias?.text === 'node-01',
97
+ };
98
+
99
+ // Verify card path skipped — no R211 card-mode texts at >16 nodes
100
+ const cardAliasCount = await chromium.launch({ headless: true }).then(async (b2) => {
101
+ const ctx2 = await b2.newContext();
102
+ await ctx2.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
103
+ await ctx2.addInitScript(() => {
104
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
105
+ });
106
+ await ctx2.route('**/api/hub/status*', async (route) => {
107
+ const r = await route.fetch();
108
+ const bj = await r.json();
109
+ const nid = (bj.sessions || [])[0]?.network_id || 'default';
110
+ await route.fulfill({ response: r, json: { ...bj, sessions: aliases.map((alias, i) => ({
111
+ alias, status: statuses[i], model: 'claude-opus-4', runtime: 'claude-code-cli',
112
+ network_id: nid, project_dir: null,
113
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
114
+ })) } });
115
+ });
116
+ await ctx2.route('**/api/hub/messages*', (rr) => rr.fulfill({ json: { messages: [] } }));
117
+ await ctx2.route('**/api/hub/tasks*', (rr) => rr.fulfill({ json: { tasks: [] } }));
118
+ const p2 = await ctx2.newPage();
119
+ await p2.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
120
+ await p2.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 17, { timeout: 30000 });
121
+ await p2.waitForTimeout(300);
122
+ const c = await p2.evaluate(() =>
123
+ document.querySelectorAll('[data-node-alias-text]').length);
124
+ await b2.close();
125
+ return c;
126
+ });
127
+ results.card_path_skipped = cardAliasCount === 0;
128
+
129
+ const ok = Object.values(results).every(Boolean);
130
+ console.log(`${ok ? '✅' : '❌'} dense alias fill transition:`, JSON.stringify(results),
131
+ `\n sample (first 3):`, probe.slice(0, 3),
132
+ `\n cardAliasCount:`, cardAliasCount);
133
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,125 @@
1
+ /* Round 169 verification: keyboard '+'/'−' and chrome zoom in/out
2
+ * buttons arm the R168 smoothView transition.
3
+ *
4
+ * R168 introduced one-shot smoothView arming on resetView/fitView.
5
+ * R169 extends the same arming to the three other DISCRETE zoom
6
+ * surfaces (one keypress / button-click = one 1.2× step):
7
+ * keyboard '+' / '=' / '−' / '_'
8
+ * chrome zoom-in / zoom-out buttons
9
+ *
10
+ * Wheel zoom stays direct (every tick = live, no transition lag).
11
+ * Wrapper:
12
+ * const zoomByDiscrete = (factor) => {
13
+ * setSmoothView(true);
14
+ * setTimeout(() => setSmoothView(false), 350);
15
+ * zoomBy(factor);
16
+ * };
17
+ *
18
+ * Test:
19
+ * 1. Idle: smooth=false, no transition
20
+ * 2. Press '+': smooth=true, transition 'transform 300ms ease-out'
21
+ * zoom 100% → 120%
22
+ * 3. Wait 450ms: smooth=false, transition cleared
23
+ * 4. Click chrome zoom-in button: smooth=true again (same path)
24
+ * 5. Press '−': smooth=true, zoom back toward 100%
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
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
39
+ await ctx.route('**/api/hub/status*', async (route) => {
40
+ const r = await route.fetch();
41
+ const b = await r.json();
42
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
43
+ const mk = (alias) => ({
44
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
45
+ network_id: nid, project_dir: null,
46
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
47
+ });
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
49
+ });
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
51
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
52
+
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
56
+ await page.waitForSelector('[data-topo-viewport]', { timeout: 10000 });
57
+ await page.waitForTimeout(400);
58
+
59
+ const probeViewport = () => page.evaluate(() => {
60
+ const g = document.querySelector('[data-topo-viewport]');
61
+ return {
62
+ smooth: g?.getAttribute('data-topo-viewport-smooth'),
63
+ transition: g?.style?.transition || '',
64
+ };
65
+ });
66
+ const probeZoom = () => page.evaluate(() =>
67
+ document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
68
+
69
+ const idle = await probeViewport();
70
+ const zoomIdle = await probeZoom();
71
+
72
+ // Press '+' → keyboard zoom-in. Probe within the 350ms window.
73
+ await page.keyboard.press('+');
74
+ await page.waitForTimeout(50);
75
+ const duringKbZoomIn = await probeViewport();
76
+ const zoomAfterKb = await probeZoom();
77
+
78
+ // Wait past the 350ms window
79
+ await page.waitForTimeout(450);
80
+ const afterKbZoomIn = await probeViewport();
81
+
82
+ // Click chrome zoom-in button → same arming path
83
+ await page.locator('[data-topo-chrome-zoom-in]').click();
84
+ await page.waitForTimeout(50);
85
+ const duringChromeZoomIn = await probeViewport();
86
+ const zoomAfterChromeIn = await probeZoom();
87
+
88
+ // Wait past window then press '−' (keyboard zoom-out)
89
+ await page.waitForTimeout(450);
90
+ await page.keyboard.press('-');
91
+ await page.waitForTimeout(50);
92
+ const duringKbZoomOut = await probeViewport();
93
+ const zoomAfterKbOut = await probeZoom();
94
+
95
+ await browser.close();
96
+
97
+ const results = {
98
+ viewport_found: idle !== null,
99
+ idle_smooth_false: idle.smooth === 'false',
100
+ idle_no_transition: !idle.transition.includes('transform'),
101
+ idle_zoom_100: zoomIdle === '100%',
102
+
103
+ kb_plus_armed_smooth: duringKbZoomIn.smooth === 'true',
104
+ kb_plus_transition_set: duringKbZoomIn.transition.includes('transform 300ms'),
105
+ kb_plus_zoom_increased: zoomAfterKb !== '100%',
106
+
107
+ after_kb_smooth_cleared: afterKbZoomIn.smooth === 'false',
108
+ after_kb_no_transition: !afterKbZoomIn.transition.includes('transform'),
109
+
110
+ chrome_in_armed_smooth: duringChromeZoomIn.smooth === 'true',
111
+ chrome_in_transition_set: duringChromeZoomIn.transition.includes('transform 300ms'),
112
+ chrome_in_zoom_increased: zoomAfterChromeIn !== zoomAfterKb,
113
+
114
+ kb_minus_armed_smooth: duringKbZoomOut.smooth === 'true',
115
+ kb_minus_transition_set: duringKbZoomOut.transition.includes('transform 300ms'),
116
+ kb_minus_zoom_decreased: zoomAfterKbOut !== zoomAfterChromeIn,
117
+ };
118
+ const ok = Object.values(results).every(Boolean);
119
+ console.log(`${ok ? '✅' : '❌'} discrete zoom smooth:`, JSON.stringify(results),
120
+ `\n idle =`, idle, `zoom=${zoomIdle}`,
121
+ `\n '+' fired =`, duringKbZoomIn, `zoom=${zoomAfterKb}`,
122
+ `\n after '+' =`, afterKbZoomIn,
123
+ `\n chrome-in fired =`, duringChromeZoomIn, `zoom=${zoomAfterChromeIn}`,
124
+ `\n '-' fired =`, duringKbZoomOut, `zoom=${zoomAfterKbOut}`);
125
+ process.exit(ok ? 0 : 1);