@sleep2agi/agent-network-dashboard 0.4.6-preview.33 → 0.4.6-preview.331

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 (496) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +41 -41
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +14 -14
  25. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +14 -14
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +14 -14
  41. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +2 -2
  49. package/.next/server/app/logs.rsc +14 -14
  50. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +2 -2
  58. package/.next/server/app/messages.rsc +14 -14
  59. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +2 -2
  67. package/.next/server/app/node.rsc +14 -14
  68. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +2 -2
  76. package/.next/server/app/nodes.rsc +14 -14
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  83. package/.next/server/app/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs.html +2 -2
  86. package/.next/server/app/server-logs.rsc +14 -14
  87. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  88. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  89. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  90. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  91. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  92. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  93. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/settings/networks.html +2 -2
  95. package/.next/server/app/settings/networks.rsc +14 -14
  96. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  97. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  99. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  103. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  105. package/.next/server/app/settings/tokens.html +2 -2
  106. package/.next/server/app/settings/tokens.rsc +14 -14
  107. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  108. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  109. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  110. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  111. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  112. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  113. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  114. package/.next/server/app/settings.html +2 -2
  115. package/.next/server/app/settings.rsc +14 -14
  116. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  117. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  119. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  122. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/tasks.html +2 -2
  125. package/.next/server/app/tasks.rsc +14 -14
  126. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  127. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  128. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  129. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  130. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  131. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  132. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  133. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  134. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  135. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  136. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  137. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  141. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  142. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  143. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  144. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  145. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  146. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  147. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  148. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  149. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  150. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  151. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  160. package/.next/server/middleware-build-manifest.js +3 -3
  161. package/.next/server/pages/404.html +2 -2
  162. package/.next/server/pages/500.html +1 -1
  163. package/.next/server/server-reference-manifest.js +1 -1
  164. package/.next/server/server-reference-manifest.json +1 -1
  165. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  166. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  167. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  168. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  169. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  170. package/.next/static/chunks/07lsi~s-c0wvh.css +2 -0
  171. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  172. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  173. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  174. package/.next/static/chunks/0mpa-fgyi2k78.js +1 -0
  175. package/.next/static/chunks/0nlhul7k4hwil.js +1 -0
  176. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  177. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  178. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  179. package/.next/static/chunks/1851sbdemt00t.js +4 -0
  180. package/.next/trace +2 -2
  181. package/.next/trace-build +1 -1
  182. package/app/components/ChatPopover.tsx +38 -2
  183. package/app/components/CommandPalette.tsx +107 -0
  184. package/app/components/HelpOverlay.tsx +111 -1
  185. package/app/components/TopoGraph.tsx +6197 -262
  186. package/app/components/utils.ts +8 -7
  187. package/app/globals.css +110 -5
  188. package/app/lib/time.ts +36 -0
  189. package/app/lib/vendorIdentity.ts +14 -3
  190. package/package.json +2 -2
  191. package/public/sleep2agi-logo.svg +11 -0
  192. package/public/vendors/claude.svg +11 -0
  193. package/public/vendors/minimax.svg +11 -0
  194. package/public/vendors/openai.svg +12 -0
  195. package/screenshots/r293-p0/after-full.png +0 -0
  196. package/screenshots/r293-p0/after-topo.png +0 -0
  197. package/screenshots/r293-p0/baseline-full.png +0 -0
  198. package/screenshots/r293-p0/baseline-topo.png +0 -0
  199. package/screenshots/r293-p0/logo-integrated-full.png +0 -0
  200. package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
  201. package/screenshots/r293-p0/v0.10.0-hero3-rfc.md +230 -0
  202. package/scripts/p0-topo-screenshot.mjs +82 -0
  203. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  204. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  205. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  206. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  207. package/scripts/topo-active-links-chip-test.mjs +73 -0
  208. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  209. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  210. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  211. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  212. package/scripts/topo-auto-fit-test.mjs +73 -0
  213. package/scripts/topo-brand-logo-transition-test.mjs +94 -0
  214. package/scripts/topo-brand-watermark-test.mjs +97 -0
  215. package/scripts/topo-canvas-aria-test.mjs +108 -0
  216. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  217. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  218. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  219. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  220. package/scripts/topo-chip-focus-test.mjs +95 -0
  221. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  222. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  223. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  224. package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
  225. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  226. package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
  227. package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
  228. package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
  229. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  230. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  231. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  232. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  233. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  234. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  235. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  236. package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
  237. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  238. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  239. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  240. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  241. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  242. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  243. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  244. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  245. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  246. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  247. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  248. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  249. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  250. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  251. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  252. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  253. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  254. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  255. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  256. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  257. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  258. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  259. package/scripts/topo-edge-fadein-test.mjs +106 -0
  260. package/scripts/topo-edge-focus-test.mjs +113 -0
  261. package/scripts/topo-edge-hover-test.mjs +93 -0
  262. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  263. package/scripts/topo-edge-pill-test.mjs +105 -0
  264. package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
  265. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  266. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  267. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  268. package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
  269. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  270. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  271. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  272. package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
  273. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  274. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  275. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  276. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  277. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  278. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  279. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  280. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  281. package/scripts/topo-fit-key-test.mjs +106 -0
  282. package/scripts/topo-freshness-chip-test.mjs +74 -0
  283. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  284. package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
  285. package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
  286. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  287. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  288. package/scripts/topo-group-count-test.mjs +6 -1
  289. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  290. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  291. package/scripts/topo-group-label-hover-test.mjs +119 -0
  292. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  293. package/scripts/topo-group-label-tint-test.mjs +99 -0
  294. package/scripts/topo-group-label-title-test.mjs +83 -0
  295. package/scripts/topo-group-pin-test.mjs +119 -0
  296. package/scripts/topo-group-pip-fadein-test.mjs +100 -0
  297. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  298. package/scripts/topo-group-status-pips-test.mjs +103 -0
  299. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  300. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  301. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  302. package/scripts/topo-groupbox-live-test.mjs +100 -0
  303. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  304. package/scripts/topo-groupbox-test.mjs +4 -2
  305. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  306. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  307. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  308. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  309. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  310. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  311. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  312. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  313. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  314. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  315. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  316. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  317. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  318. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  319. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  320. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  321. package/scripts/topo-hub-a11y-test.mjs +112 -0
  322. package/scripts/topo-hub-busyness-test.mjs +100 -0
  323. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  324. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  325. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  326. package/scripts/topo-hub-fadein-test.mjs +88 -0
  327. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  328. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  329. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  330. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  331. package/scripts/topo-hub-title-test.mjs +93 -0
  332. package/scripts/topo-hub-working-count-test.mjs +99 -0
  333. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  334. package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
  335. package/scripts/topo-kicker-color-test.mjs +93 -0
  336. package/scripts/topo-kicker-tracking-test.mjs +101 -0
  337. package/scripts/topo-kicker-weight-test.mjs +98 -0
  338. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  339. package/scripts/topo-label-lift-test.mjs +119 -0
  340. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  341. package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
  342. package/scripts/topo-layout-key-test.mjs +91 -0
  343. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  344. package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
  345. package/scripts/topo-layout-toggle-test.mjs +131 -0
  346. package/scripts/topo-legend-compress-test.mjs +107 -0
  347. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  348. package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
  349. package/scripts/topo-legend-count-tabular-test.mjs +87 -0
  350. package/scripts/topo-legend-count-test.mjs +88 -0
  351. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  352. package/scripts/topo-legend-count-weight-test.mjs +86 -0
  353. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  354. package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
  355. package/scripts/topo-legend-hover-test.mjs +113 -0
  356. package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
  357. package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
  358. package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
  359. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  360. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  361. package/scripts/topo-legend-pin-test.mjs +128 -0
  362. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  363. package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
  364. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  365. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  366. package/scripts/topo-legend-row-title-test.mjs +102 -0
  367. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  368. package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
  369. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  370. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  371. package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
  372. package/scripts/topo-minimap-test.mjs +68 -0
  373. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  374. package/scripts/topo-mobile-chips-test.mjs +103 -0
  375. package/scripts/topo-monogram-font-test.mjs +112 -0
  376. package/scripts/topo-monogram-stroke-test.mjs +101 -0
  377. package/scripts/topo-node-a11y-test.mjs +107 -0
  378. package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
  379. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  380. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  381. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  382. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  383. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  384. package/scripts/topo-node-title-peers-test.mjs +107 -0
  385. package/scripts/topo-node-title-test.mjs +101 -0
  386. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  387. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  388. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  389. package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
  390. package/scripts/topo-overlap-test.mjs +32 -5
  391. package/scripts/topo-panel-elevation-test.mjs +76 -0
  392. package/scripts/topo-panel-fadein-test.mjs +102 -0
  393. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  394. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  395. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  396. package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
  397. package/scripts/topo-panel-title-tracking-test.mjs +78 -0
  398. package/scripts/topo-particle-stagger-test.mjs +82 -0
  399. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  400. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  401. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  402. package/scripts/topo-pin-intersection-test.mjs +121 -0
  403. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  404. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  405. package/scripts/topo-pin-persistence-test.mjs +105 -0
  406. package/scripts/topo-popover-meta-test.mjs +78 -0
  407. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  408. package/scripts/topo-pressure-bar-test.mjs +69 -0
  409. package/scripts/topo-pressure-hover-test.mjs +123 -0
  410. package/scripts/topo-pressure-pin-test.mjs +119 -0
  411. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  412. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  413. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  414. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  415. package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
  416. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  417. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  418. package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
  419. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  420. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  421. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  422. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  423. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  424. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  425. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  426. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  427. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  428. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  429. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  430. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  431. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  432. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  433. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  434. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  435. package/scripts/topo-recent-row-title-test.mjs +112 -0
  436. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  437. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  438. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  439. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  440. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  441. package/scripts/topo-reduced-motion-test.mjs +77 -0
  442. package/scripts/topo-reset-spin-test.mjs +120 -0
  443. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  444. package/scripts/topo-spokes-retired-test.mjs +93 -0
  445. package/scripts/topo-starfield-density-test.mjs +89 -0
  446. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  447. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  448. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  449. package/scripts/topo-svg-focus-test.mjs +112 -0
  450. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  451. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  452. package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
  453. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  454. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  455. package/scripts/topo-tier-ring-test.mjs +69 -0
  456. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  457. package/scripts/topo-tier-stagger-test.mjs +89 -0
  458. package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
  459. package/scripts/topo-title-tracking-test.mjs +98 -0
  460. package/scripts/topo-titleblock-gap-test.mjs +86 -0
  461. package/scripts/topo-titleblock-margin-test.mjs +83 -0
  462. package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
  463. package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
  464. package/scripts/topo-tz-ghost-test.mjs +69 -0
  465. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  466. package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
  467. package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
  468. package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
  469. package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
  470. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  471. package/scripts/topo-vendor-pill-test.mjs +125 -0
  472. package/scripts/topo-vendor-pin-test.mjs +125 -0
  473. package/scripts/topo-view-smooth-test.mjs +131 -0
  474. package/scripts/topo-watermark-tracking-test.mjs +99 -0
  475. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  476. package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
  477. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  478. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  479. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  480. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  481. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  482. package/.next/static/chunks/0_8snedeata9k.js +0 -1
  483. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  484. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  485. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  486. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  487. package/.next/static/chunks/0kwf..oq7.27x.js +0 -7
  488. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  489. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  490. package/.next/static/chunks/0sjqjenh7~k-j.js +0 -1
  491. package/.next/static/chunks/0y8v4grez.a76.js +0 -1
  492. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  493. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  494. /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_buildManifest.js +0 -0
  495. /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_clientMiddlewareManifest.js +0 -0
  496. /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_ssgManifest.js +0 -0
@@ -0,0 +1,91 @@
1
+ /* Round 276 verification: outer-ring orbit particles retired by default.
2
+ *
3
+ * R50 introduced 4 cyan particles orbiting r=330 (outer ring edge) at
4
+ * 16s revolution. R131 added busy-bucket speed (workingCount → 16/14/
5
+ * 12/10s). R216 added busy-bucket opacity (0.5/0.7/0.85/1.0).
6
+ *
7
+ * Pre-R276 the orbit family rendered 4 <g data-topo-orbit-bucket=...>
8
+ * groups whenever the canvas was non-light theme. Each particle was a
9
+ * 2.2-2.8 px cyan circle with animateTransform rotation, opacity gated
10
+ * by busy bucket.
11
+ *
12
+ * The orbit family encoded workingCount busyness via speed + opacity,
13
+ * but that signal was ALREADY conveyed by:
14
+ * · hub halo opacity breath (R244 / R84)
15
+ * · hub digit workingCount text (R130)
16
+ * · pressure-bar working/idle/offline ratio (R31)
17
+ *
18
+ * Info-redundant decoration. For a static Twitter screenshot, 4 small
19
+ * dots at the canvas outer edge contributed to "乱" without
20
+ * proportional signal. R276 gates the render block with `false &&` so
21
+ * the code remains in place (commented context + rollback-friendly)
22
+ * but nothing renders.
23
+ *
24
+ * Test scope:
25
+ * 1. No element matches `[data-topo-orbit-bucket]` in DOM (orbit
26
+ * particles absent).
27
+ * 2. No element matches `[data-topo-orbit-opacity]` (R216 attr).
28
+ * 3. R275 freshness chip still absent at fresh state (regression —
29
+ * simplification family continues).
30
+ * 4. R273 Layout toggle Grid inactive has hover:text-cyan-300
31
+ * (regression).
32
+ */
33
+ import { chromium } from 'playwright';
34
+ import { readFileSync } from 'node:fs';
35
+
36
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
37
+ const browser = await chromium.launch({ headless: true });
38
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
39
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
40
+ await ctx.addInitScript(() => {
41
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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
+ await route.fulfill({ response: r, json: { ...b, sessions: [
54
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
55
+ ] } });
56
+ });
57
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
58
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
59
+
60
+ const page = await ctx.newPage();
61
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
62
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
63
+ await page.waitForSelector('[data-topo-chrome-layout="grid"]', { timeout: 10000 });
64
+ await page.waitForTimeout(500);
65
+
66
+ const probe = await page.evaluate(() => {
67
+ const orbits = document.querySelectorAll('[data-topo-orbit-bucket]');
68
+ const orbitsOpacity = document.querySelectorAll('[data-topo-orbit-opacity]');
69
+ const freshnessChip = document.querySelector('[data-freshness-chip]');
70
+ const grid = document.querySelector('[data-topo-chrome-layout="grid"]');
71
+ return {
72
+ orbitCount: orbits.length,
73
+ orbitOpacityCount: orbitsOpacity.length,
74
+ freshnessPresent: freshnessChip !== null,
75
+ gridClasses: grid ? grid.className.toString() : null,
76
+ };
77
+ });
78
+ await browser.close();
79
+
80
+ const results = {
81
+ orbit_bucket_absent: probe.orbitCount === 0,
82
+ orbit_opacity_absent: probe.orbitOpacityCount === 0,
83
+ r275_freshness_absent_at_fresh: probe.freshnessPresent === false,
84
+ r273_grid_has_cyan_hover_text: (probe.gridClasses || '').includes('hover:text-cyan-300'),
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} orbit particles retired:`, JSON.stringify(results),
88
+ '\n orbit count (expect 0):', probe.orbitCount,
89
+ '\n orbit-opacity count (expect 0):', probe.orbitOpacityCount,
90
+ '\n freshness chip absent (R275):', !probe.freshnessPresent);
91
+ process.exit(ok ? 0 : 1);
@@ -3,7 +3,10 @@
3
3
  * - node ↔ node — status rings don't intersect
4
4
  * - node ↔ panel — no node circle intersects an overlay panel rect
5
5
  * - node ↔ group-label — no group-box label text covers a node
6
- * - group-box ↔ box — no two group boxes overlap */
6
+ * - group-box ↔ box — no two group boxes overlap
7
+ * - label ↔ label — no two text labels overlap (Round 27 — caught
8
+ * the dense-node-label vs group-label collision
9
+ * Vincent reported in preview.29) */
7
10
  import { chromium } from 'playwright';
8
11
  import { readFileSync, mkdirSync } from 'node:fs';
9
12
 
@@ -80,11 +83,24 @@ async function check(layout) {
80
83
  label: { x: bb.x, y: bb.y, w: bb.width, h: bb.height },
81
84
  };
82
85
  });
83
- return { nodes, panels, groups };
86
+ // Round 27 / P0: every visible label bbox (node alias text + group
87
+ // headings) so we can assert label↔label clearance. Skip elements
88
+ // with empty bboxes (e.g., text inside an unrendered <g>).
89
+ const allLabels = [];
90
+ for (const t of svg.querySelectorAll('g[data-node] text, g[data-group] text')) {
91
+ const bb = t.getBBox();
92
+ if (bb.width === 0 || bb.height === 0) continue;
93
+ allLabels.push({
94
+ kind: t.closest('g[data-group]') ? 'group' : 'node',
95
+ owner: (t.closest('g[data-group]')?.getAttribute('data-group')) || (t.closest('g[data-node]')?.getAttribute('data-node')) || '?',
96
+ x: bb.x, y: bb.y, w: bb.width, h: bb.height,
97
+ });
98
+ }
99
+ return { nodes, panels, groups, allLabels };
84
100
  });
85
101
 
86
102
  const TOL = 2; // px slack — touching edges is fine, real overlap is not
87
- let nodeNode = 0, nodePanel = 0, nodeLabel = 0, boxBox = 0;
103
+ let nodeNode = 0, nodePanel = 0, nodeLabel = 0, boxBox = 0, labelLabel = 0;
88
104
 
89
105
  // node ↔ node
90
106
  for (let i = 0; i < data.nodes.length; i++) {
@@ -119,12 +135,23 @@ async function check(layout) {
119
135
  overlaps1D(a.y, a.y + a.h, b.y, b.y + b.h, TOL)) boxBox++;
120
136
  }
121
137
  }
138
+ // label ↔ label (Round 27)
139
+ // Same-node node-labels (e.g. alias + sub-status in the full card) are
140
+ // intentionally close and adjacent — skip pairs that share an owner.
141
+ for (let i = 0; i < data.allLabels.length; i++) {
142
+ for (let j = i + 1; j < data.allLabels.length; j++) {
143
+ const a = data.allLabels[i], b = data.allLabels[j];
144
+ if (a.owner === b.owner) continue;
145
+ if (overlaps1D(a.x, a.x + a.w, b.x, b.x + b.w, TOL) &&
146
+ overlaps1D(a.y, a.y + a.h, b.y, b.y + b.h, TOL)) labelLabel++;
147
+ }
148
+ }
122
149
 
123
150
  await page.screenshot({ path: `/tmp/anet-issue-112/overlap-${layout}.png` });
124
151
  await ctx.close();
125
- const ok = nodeNode === 0 && nodePanel === 0 && nodeLabel === 0 && boxBox === 0;
152
+ const ok = nodeNode === 0 && nodePanel === 0 && nodeLabel === 0 && boxBox === 0 && labelLabel === 0;
126
153
  console.log(`${ok ? '✅' : '❌'} [${layout}] nodes=${data.nodes.length} groups=${data.groups.length} — ` +
127
- `node↔node:${nodeNode} node↔panel:${nodePanel} node↔label:${nodeLabel} box↔box:${boxBox}`);
154
+ `node↔node:${nodeNode} node↔panel:${nodePanel} node↔label:${nodeLabel} box↔box:${boxBox} label↔label:${labelLabel}`);
128
155
  return ok;
129
156
  }
130
157
 
@@ -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,102 @@
1
+ /* Round 175 verification: corner panels fade-in after the
2
+ * canvas content reveal.
3
+ *
4
+ * Pre-R175 the recent-signal (top-left) + legend (top-right)
5
+ * panels appeared instantly while nodes/edges/group boxes/tier
6
+ * rings staggered in around them. The panels felt like 'already
7
+ * there' before the content showed up.
8
+ *
9
+ * R175 adds .anet-fade-in + 700ms / 800ms animation-delay on
10
+ * the two panel <g> wrappers — fifth surface in the first-paint
11
+ * reveal family (after R9 nodes / R172 edges / R173 group boxes
12
+ * / R174 tier rings).
13
+ *
14
+ * Test:
15
+ * 1. Mock 3 sessions in ring layout
16
+ * 2. Probe [data-topo-panel="recent"] and [="legend"]
17
+ * 3. Both have .anet-fade-in className
18
+ * 4. recent panel animationDelay = 700ms
19
+ * 5. legend panel animationDelay = 800ms
20
+ * 6. data-topo-panel-fade-delay attributes match
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 {
32
+ localStorage.setItem('anet-theme', 'cyber');
33
+ sessionStorage.setItem('anet_v3_auth', '1');
34
+ localStorage.setItem('anet-topo-layout', 'ring');
35
+ } 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-panel="recent"]', { timeout: 10000 });
57
+ await page.waitForSelector('[data-topo-panel="legend"]', { timeout: 10000 });
58
+ await page.waitForTimeout(400);
59
+
60
+ const probe = await page.evaluate(() => {
61
+ const grab = (sel) => {
62
+ const el = document.querySelector(sel);
63
+ if (!el) return null;
64
+ return {
65
+ hasFadeClass: (el.getAttribute('class') || '').includes('anet-fade-in'),
66
+ fadeDelayAttr: parseFloat(el.getAttribute('data-topo-panel-fade-delay') || ''),
67
+ animationDelay: el.style.animationDelay || getComputedStyle(el).animationDelay,
68
+ };
69
+ };
70
+ return {
71
+ recent: grab('[data-topo-panel="recent"]'),
72
+ legend: grab('[data-topo-panel="legend"]'),
73
+ };
74
+ });
75
+
76
+ await browser.close();
77
+
78
+ const parseDelay = (s) => {
79
+ if (!s) return null;
80
+ if (s.endsWith('ms')) return parseFloat(s);
81
+ if (s.endsWith('s')) return parseFloat(s) * 1000;
82
+ return parseFloat(s);
83
+ };
84
+ const recentDelay = parseDelay(probe.recent?.animationDelay);
85
+ const legendDelay = parseDelay(probe.legend?.animationDelay);
86
+
87
+ const results = {
88
+ recent_panel_found: probe.recent !== null,
89
+ legend_panel_found: probe.legend !== null,
90
+ recent_has_fade_class: probe.recent?.hasFadeClass === true,
91
+ legend_has_fade_class: probe.legend?.hasFadeClass === true,
92
+ recent_delay_attr_700: probe.recent?.fadeDelayAttr === 700,
93
+ legend_delay_attr_800: probe.legend?.fadeDelayAttr === 800,
94
+ recent_anim_delay_700ms: recentDelay === 700,
95
+ legend_anim_delay_800ms: legendDelay === 800,
96
+ legend_lags_recent_100ms: legendDelay - recentDelay === 100,
97
+ };
98
+ const ok = Object.values(results).every(Boolean);
99
+ console.log(`${ok ? '✅' : '❌'} panel fade-in:`, JSON.stringify(results),
100
+ `\n recent =`, probe.recent, `delay=${recentDelay}`,
101
+ `\n legend =`, probe.legend, `delay=${legendDelay}`);
102
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,132 @@
1
+ /* Round 190 verification: R129 hot-tail tspan in recent-signal
2
+ * panel header gets anet-fade-in for smooth entrance.
3
+ *
4
+ * Pre-R190 the tspan mounted/unmounted on hotFlowCount > 0 —
5
+ * snap. R190 keeps the conditional render (so the tspan
6
+ * doesn't consume layout when no hot flows) but adds the R3
7
+ * .anet-fade-in CSS animation that plays once on mount.
8
+ *
9
+ * Test scenarios:
10
+ * A. 0 hot flows (just a few cold ones):
11
+ * [data-recent-panel-hot-count] NOT present
12
+ * B. 1 hot flow (count=12):
13
+ * tspan present with text '· 1 hot', className contains
14
+ * 'anet-fade-in', data-recent-panel-hot-count='1'
15
+ * C. 2 hot flows (count=15 + count=20):
16
+ * data-recent-panel-hot-count='2', text '· 2 hot'
17
+ */
18
+ import { chromium } from 'playwright';
19
+ import { readFileSync } from 'node:fs';
20
+
21
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
22
+ const browser = await chromium.launch({ headless: true });
23
+
24
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
28
+ });
29
+
30
+ async function run(scenario) {
31
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
32
+ await ctx.unroute('**/api/hub/status*').catch(() => {});
33
+ await ctx.unroute('**/api/hub/messages*').catch(() => {});
34
+ await ctx.unroute('**/api/hub/tasks*').catch(() => {});
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [
45
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
46
+ ] } });
47
+ });
48
+ const now = Date.now();
49
+ const msgs = scenario(now);
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
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 === 4, { timeout: 30000 });
56
+ await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
57
+ await page.waitForTimeout(400);
58
+
59
+ const out = await page.evaluate(() => {
60
+ const tail = document.querySelector('[data-recent-panel-hot-count]');
61
+ return {
62
+ tailPresent: !!tail,
63
+ tailText: tail?.textContent || null,
64
+ tailCount: tail?.getAttribute('data-recent-panel-hot-count'),
65
+ tailClassName: tail?.getAttribute('class') || '',
66
+ // R223 added: visibility attribute for always-mount-with-opacity-
67
+ // gate. "no tail" is either tailPresent=false (legacy conditional
68
+ // mount, no longer current) OR tailVisible='false' (R223 always-
69
+ // mount with opacity 0).
70
+ tailVisible: tail?.getAttribute('data-recent-panel-hot-visible'),
71
+ };
72
+ });
73
+ await page.close();
74
+ return out;
75
+ }
76
+
77
+ // Scenario A: 0 hot — 1 cold flow
78
+ const a = await run((now) => {
79
+ const m = [];
80
+ for (let i = 0; i < 4; i++) {
81
+ m.push({ id: `cold${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
82
+ network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString() });
83
+ }
84
+ return m;
85
+ });
86
+
87
+ // Scenario B: 1 hot flow alpha→beta count=12
88
+ const b = await run((now) => {
89
+ const m = [];
90
+ for (let i = 0; i < 12; i++) {
91
+ m.push({ id: `hot${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
92
+ network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString() });
93
+ }
94
+ return m;
95
+ });
96
+
97
+ // Scenario C: 2 hot flows
98
+ const c = await run((now) => {
99
+ const m = [];
100
+ for (let i = 0; i < 15; i++) {
101
+ m.push({ id: `h1-${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
102
+ network_id: 'default', created_at: new Date(now - (5000 + i * 50)).toISOString() });
103
+ }
104
+ for (let i = 0; i < 20; i++) {
105
+ m.push({ id: `h2-${i}`, from_alias: 'gamma', to_alias: 'delta', content: 'hi',
106
+ network_id: 'default', created_at: new Date(now - (15000 + i * 50)).toISOString() });
107
+ }
108
+ return m;
109
+ });
110
+
111
+ await browser.close();
112
+
113
+ const results = {
114
+ // R223 update: hot-tail is always-mounted now with opacity-gate;
115
+ // "no tail" means either not in DOM (legacy) OR present-but-hidden
116
+ // (always-mount visible='false'). Accept both forms.
117
+ a_no_tail: a.tailPresent === false || a.tailVisible === 'false',
118
+ b_tail_present: b.tailPresent === true,
119
+ b_tail_text: /·\s*1\s*hot/.test(b.tailText || ''),
120
+ b_tail_count_1: b.tailCount === '1',
121
+ b_has_fade_class: b.tailClassName.includes('anet-fade-in'),
122
+ c_tail_present: c.tailPresent === true,
123
+ c_tail_text: /·\s*2\s*hot/.test(c.tailText || ''),
124
+ c_tail_count_2: c.tailCount === '2',
125
+ c_has_fade_class: c.tailClassName.includes('anet-fade-in'),
126
+ };
127
+ const ok = Object.values(results).every(Boolean);
128
+ console.log(`${ok ? '✅' : '❌'} panel hot-tail fade:`, JSON.stringify(results),
129
+ `\n A (0 hot):`, a,
130
+ `\n B (1 hot):`, b,
131
+ `\n C (2 hot):`, c);
132
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,118 @@
1
+ /* Round 135 verification: both overlay panels (recent-signal +
2
+ * legend) drop-shadow boosts on mouseenter, restores on leave.
3
+ * Mirrors R18's KPI-card hover-elevation from the Overview page.
4
+ *
5
+ * Why both panels: each panel chrome already hosts interactive
6
+ * rows (R56/R116 recent rows, R55/R61 legend rows) and footer
7
+ * nav (R133). Hover-lift signals "this whole panel is alive"
8
+ * without misleading users that the panel itself is clickable —
9
+ * the underlying click affordances are still the rows + footer.
10
+ *
11
+ * State transitions tested:
12
+ * 1. Neither panel hovered → both data-topo-panel-hovered="false",
13
+ * both rect filters = baseline (2px/6px blur)
14
+ * 2. Hover recent → that panel hovered=true, filter boosted
15
+ * (4px/12px blur); legend still baseline
16
+ * 3. Leave recent (move far away) → recent restored
17
+ * 4. Hover legend → legend hovered=true, filter boosted
18
+ * 5. Leave legend → legend restored
19
+ *
20
+ * Filter inline-string detection: the boosted variant carries the
21
+ * `4px 12px` magic numbers in its drop-shadow argument.
22
+ */
23
+ import { chromium } from 'playwright';
24
+ import { readFileSync } from 'node:fs';
25
+
26
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
27
+ const browser = await chromium.launch({ headless: true });
28
+
29
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
30
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
31
+ await ctx.addInitScript(() => {
32
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
33
+ });
34
+
35
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
36
+ await ctx.route('**/api/hub/status*', async (route) => {
37
+ const r = await route.fetch();
38
+ const b = await r.json();
39
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
40
+ const mk = (alias, status) => ({
41
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
42
+ network_id: nid, project_dir: null,
43
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
44
+ });
45
+ await route.fulfill({ response: r, json: { ...b, sessions: [
46
+ mk('alpha', 'working'), mk('beta', 'idle'), mk('gamma', 'working'),
47
+ ] } });
48
+ });
49
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
50
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
54
+ await page.waitForSelector('[data-topo-panel="recent"]', { timeout: 10000 });
55
+ await page.waitForTimeout(400);
56
+
57
+ const inspect = () => page.evaluate(() => {
58
+ const get = (key) => {
59
+ const g = document.querySelector(`[data-topo-panel="${key}"]`);
60
+ const rect = g?.querySelector('rect[data-topo-panel-elevation]');
61
+ return {
62
+ hovered: g?.getAttribute('data-topo-panel-hovered'),
63
+ filter: (rect?.getAttribute('style') || ''),
64
+ };
65
+ };
66
+ return { recent: get('recent'), legend: get('legend') };
67
+ });
68
+
69
+ // State 1 — neither hovered
70
+ const s1 = await inspect();
71
+
72
+ // State 2 — hover recent
73
+ await page.locator('[data-topo-panel="recent"]').hover();
74
+ await page.waitForTimeout(300);
75
+ const s2 = await inspect();
76
+
77
+ // State 3 — leave recent (move far away from both panels)
78
+ await page.mouse.move(700, 350);
79
+ await page.waitForTimeout(300);
80
+ const s3 = await inspect();
81
+
82
+ // State 4 — hover legend
83
+ await page.locator('[data-topo-panel="legend"]').hover();
84
+ await page.waitForTimeout(300);
85
+ const s4 = await inspect();
86
+
87
+ // State 5 — leave legend
88
+ await page.mouse.move(500, 350);
89
+ await page.waitForTimeout(300);
90
+ const s5 = await inspect();
91
+
92
+ await browser.close();
93
+
94
+ const isBoosted = (style) => /4px\s+12px/.test(style);
95
+ const isBase = (style) => /2px\s+6px/.test(style) && !/4px\s+12px/.test(style);
96
+
97
+ const results = {
98
+ s1_recent_notHovered: s1.recent.hovered === 'false',
99
+ s1_legend_notHovered: s1.legend.hovered === 'false',
100
+ s1_recent_baseShadow: isBase(s1.recent.filter),
101
+ s1_legend_baseShadow: isBase(s1.legend.filter),
102
+
103
+ s2_recent_hovered: s2.recent.hovered === 'true',
104
+ s2_recent_boosted: isBoosted(s2.recent.filter),
105
+ s2_legend_stillBase: s2.legend.hovered === 'false' && isBase(s2.legend.filter),
106
+
107
+ s3_recent_restored: s3.recent.hovered === 'false' && isBase(s3.recent.filter),
108
+
109
+ s4_legend_hovered: s4.legend.hovered === 'true',
110
+ s4_legend_boosted: isBoosted(s4.legend.filter),
111
+ s4_recent_stillBase: s4.recent.hovered === 'false' && isBase(s4.recent.filter),
112
+
113
+ s5_legend_restored: s5.legend.hovered === 'false' && isBase(s5.legend.filter),
114
+ };
115
+ const ok = Object.values(results).every(Boolean);
116
+ console.log(`${ok ? '✅' : '❌'} panel hover elevation:`, JSON.stringify(results),
117
+ `\n s1=`, s1, `\n s2=`, s2, `\n s3=`, s3, `\n s4=`, s4, `\n s5=`, s5);
118
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,85 @@
1
+ /* Round 247 verification: recent-signal + legend panel chrome
2
+ * <rect>s pick up fill + stroke + opacity in their transition
3
+ * lists, completing the per-panel theme-toggle smoothing.
4
+ *
5
+ * Pre-R247 each panel rect transition was 'filter 200ms ease-out'
6
+ * (R135 hover-elevation drop-shadow). Three theme-driven properties
7
+ * snapped on theme toggle:
8
+ * - fill: pal.legendBox.fill (cyber #020617 ↔ light #ffffff)
9
+ * - stroke: pal.legendBox.stroke (cyber #1f2937 ↔ light #e2e8f0)
10
+ * - opacity: 0.92 cyber ↔ 0.97 light
11
+ *
12
+ * R247 adds all three to the transition list at 200ms ease-out.
13
+ * Mirrors R246's per-node label-card treatment at the panel scope.
14
+ *
15
+ * Test scope per panel:
16
+ * - element present at [data-topo-panel-elevation]
17
+ * - style.transition contains filter, fill, stroke, AND opacity
18
+ * each at 200ms (or 0.2s browser-normalised)
19
+ */
20
+ import { chromium } from 'playwright';
21
+ import { readFileSync } from 'node:fs';
22
+
23
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
24
+ const browser = await chromium.launch({ headless: true });
25
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
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) => ({
36
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
46
+
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 === 4, { timeout: 30000 });
50
+ await page.waitForSelector('[data-topo-panel-elevation="recent"]', { timeout: 10000, state: 'attached' });
51
+ await page.waitForSelector('[data-topo-panel-elevation="legend"]', { timeout: 10000, state: 'attached' });
52
+ await page.waitForTimeout(300);
53
+
54
+ const probe = await page.evaluate(() => {
55
+ const grab = (panel) => {
56
+ const r = document.querySelector(`[data-topo-panel-elevation="${panel}"]`);
57
+ if (!r) return null;
58
+ return { transition: r.style.transition };
59
+ };
60
+ return {
61
+ recent: grab('recent'),
62
+ legend: grab('legend'),
63
+ };
64
+ });
65
+ await browser.close();
66
+
67
+ const hasProp = (s, prop) => new RegExp(`${prop}\\s+(?:200ms|0\\.2s)`).test(s || '');
68
+
69
+ const results = {
70
+ recent_present: probe.recent !== null,
71
+ recent_has_filter_200: hasProp(probe.recent?.transition, 'filter'),
72
+ recent_has_fill_200: hasProp(probe.recent?.transition, 'fill'),
73
+ recent_has_stroke_200: hasProp(probe.recent?.transition, 'stroke'),
74
+ recent_has_opacity_200: hasProp(probe.recent?.transition, 'opacity'),
75
+ legend_present: probe.legend !== null,
76
+ legend_has_filter_200: hasProp(probe.legend?.transition, 'filter'),
77
+ legend_has_fill_200: hasProp(probe.legend?.transition, 'fill'),
78
+ legend_has_stroke_200: hasProp(probe.legend?.transition, 'stroke'),
79
+ legend_has_opacity_200: hasProp(probe.legend?.transition, 'opacity'),
80
+ };
81
+ const ok = Object.values(results).every(Boolean);
82
+ console.log(`${ok ? '✅' : '❌'} panel theme ease:`, JSON.stringify(results),
83
+ '\n recent:', probe.recent,
84
+ '\n legend:', probe.legend);
85
+ process.exit(ok ? 0 : 1);