@sleep2agi/agent-network-dashboard 0.5.3-preview.9 → 0.5.3-preview.90

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 (243) 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 +32 -32
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +2 -2
  14. package/.next/server/app/_not-found.rsc +12 -12
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  17. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  22. package/.next/server/app/admin.html +2 -2
  23. package/.next/server/app/admin.rsc +14 -14
  24. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  25. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  26. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  27. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  28. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  30. package/.next/server/app/index.html +2 -2
  31. package/.next/server/app/index.rsc +14 -14
  32. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  33. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  34. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  35. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  36. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  37. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  38. package/.next/server/app/login.html +2 -2
  39. package/.next/server/app/login.rsc +14 -14
  40. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  41. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  42. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  43. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  44. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  45. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  46. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/logs.html +2 -2
  48. package/.next/server/app/logs.rsc +14 -14
  49. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  50. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  51. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  52. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  53. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  54. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  55. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/messages.html +2 -2
  57. package/.next/server/app/messages.rsc +14 -14
  58. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  59. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  60. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  61. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  62. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  63. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  64. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app/node.html +2 -2
  66. package/.next/server/app/node.rsc +14 -14
  67. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  68. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  69. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  70. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  71. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  72. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  73. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/nodes.html +2 -2
  75. package/.next/server/app/nodes.rsc +14 -14
  76. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  77. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  78. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  79. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  80. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  81. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  82. package/.next/server/app/page_client-reference-manifest.js +1 -1
  83. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs.html +2 -2
  85. package/.next/server/app/server-logs.rsc +14 -14
  86. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  92. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/settings/networks.html +2 -2
  94. package/.next/server/app/settings/networks.rsc +14 -14
  95. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  96. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  97. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  98. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  99. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  100. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  101. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  102. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/settings/tokens.html +2 -2
  105. package/.next/server/app/settings/tokens.rsc +14 -14
  106. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  107. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  108. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  109. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  110. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  111. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  112. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  113. package/.next/server/app/settings.html +2 -2
  114. package/.next/server/app/settings.rsc +14 -14
  115. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  116. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  117. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  118. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  119. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  120. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  121. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/tasks.html +2 -2
  124. package/.next/server/app/tasks.rsc +14 -14
  125. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  131. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  132. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  133. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -4
  134. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  135. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  136. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  141. package/.next/server/middleware-build-manifest.js +3 -3
  142. package/.next/server/pages/404.html +2 -2
  143. package/.next/server/pages/500.html +1 -1
  144. package/.next/static/chunks/0ajvyui5ltdgw.js +1 -0
  145. package/.next/static/chunks/{00d~vl~k~7f75.js → 0u9my~t.g2_an.js} +1 -1
  146. package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
  147. package/.next/static/chunks/0y3zijtrr9r81.js +4 -0
  148. package/.next/static/chunks/13~aih56vx-cf.css +2 -0
  149. package/.next/static/chunks/1857rb9ikv20f.js +1 -0
  150. package/.next/trace +2 -2
  151. package/.next/trace-build +1 -1
  152. package/app/components/ServersDrawer.tsx +16 -3
  153. package/app/components/TopoGraph.tsx +2446 -121
  154. package/app/globals.css +81 -7
  155. package/package.json +4 -4
  156. package/scripts/p157-servers-copy-test.mjs +95 -0
  157. package/scripts/topo-active-chrome-hover-text-test.mjs +107 -0
  158. package/scripts/topo-alias-glow-test.mjs +121 -0
  159. package/scripts/topo-avatar-brightness-test.mjs +116 -0
  160. package/scripts/topo-avatar-fallback-hover-test.mjs +104 -0
  161. package/scripts/topo-brand-logo-breath-test.mjs +102 -0
  162. package/scripts/topo-brand-logo-hover-brightness-test.mjs +105 -0
  163. package/scripts/topo-brand-logo-hover-rotate-test.mjs +93 -0
  164. package/scripts/topo-brand-logo-hover-test.mjs +85 -0
  165. package/scripts/topo-chip-row-digit-ls-test.mjs +135 -0
  166. package/scripts/topo-chip-row-member-alias-lit-test.mjs +154 -0
  167. package/scripts/topo-chip-row-unit-hover-tracking-test.mjs +124 -0
  168. package/scripts/topo-cluster-count-attr-test.mjs +80 -0
  169. package/scripts/topo-crescent-breath-test.mjs +104 -0
  170. package/scripts/topo-crescent-recede-test.mjs +111 -0
  171. package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
  172. package/scripts/topo-edge-badge-text-brightness-test.mjs +83 -0
  173. package/scripts/topo-edge-pill-glow-test.mjs +67 -0
  174. package/scripts/topo-filter-pill-glow-test.mjs +90 -0
  175. package/scripts/topo-fleet-density-tier-test.mjs +84 -0
  176. package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
  177. package/scripts/topo-fullscreen-attr-test.mjs +73 -0
  178. package/scripts/topo-fullscreen-icon-rotate-test.mjs +93 -0
  179. package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
  180. package/scripts/topo-group-label-brightness-test.mjs +84 -0
  181. package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
  182. package/scripts/topo-group-label-member-alias-hover-test.mjs +125 -0
  183. package/scripts/topo-group-pill-glow-test.mjs +76 -0
  184. package/scripts/topo-hub-digit-brightness-test.mjs +79 -0
  185. package/scripts/topo-hub-digit-ls-test.mjs +119 -0
  186. package/scripts/topo-hub-halo-glow-test.mjs +96 -0
  187. package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
  188. package/scripts/topo-hub-highlight-brightness-test.mjs +84 -0
  189. package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
  190. package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
  191. package/scripts/topo-hub-highlight-r-test.mjs +112 -0
  192. package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
  193. package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
  194. package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
  195. package/scripts/topo-hub-idle-breath-test.mjs +109 -0
  196. package/scripts/topo-hub-recede-test.mjs +124 -0
  197. package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
  198. package/scripts/topo-layout-hover-fw-test.mjs +98 -0
  199. package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
  200. package/scripts/topo-legend-label-fw-test.mjs +107 -0
  201. package/scripts/topo-legend-row-label-glow-test.mjs +102 -0
  202. package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
  203. package/scripts/topo-legend-swatch-member-alias-match-test.mjs +139 -0
  204. package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
  205. package/scripts/topo-node-alias-brightness-test.mjs +84 -0
  206. package/scripts/topo-node-sub-text-brightness-test.mjs +88 -0
  207. package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
  208. package/scripts/topo-orphan-box-dash-test.mjs +89 -0
  209. package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
  210. package/scripts/topo-orphan-label-italic-test.mjs +90 -0
  211. package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
  212. package/scripts/topo-panel-count-hover-ls-test.mjs +87 -0
  213. package/scripts/topo-panel-row-brightness-test.mjs +116 -0
  214. package/scripts/topo-panel-title-brightness-test.mjs +98 -0
  215. package/scripts/topo-panel-title-glow-test.mjs +111 -0
  216. package/scripts/topo-pill-x-rotate-test.mjs +96 -0
  217. package/scripts/topo-pinned-aspect-test.mjs +89 -0
  218. package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
  219. package/scripts/topo-pressure-seg-member-alias-match-test.mjs +133 -0
  220. package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
  221. package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
  222. package/scripts/topo-recent-more-fw-test.mjs +126 -0
  223. package/scripts/topo-recent-row-fw-test.mjs +115 -0
  224. package/scripts/topo-recent-row-text-glow-test.mjs +86 -0
  225. package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
  226. package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
  227. package/scripts/topo-runtime-badge-glow-test.mjs +108 -0
  228. package/scripts/topo-starfield-hue-test.mjs +109 -0
  229. package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
  230. package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
  231. package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
  232. package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
  233. package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
  234. package/scripts/topo-watermark-breath-test.mjs +100 -0
  235. package/scripts/topo-watermark-recede-test.mjs +114 -0
  236. package/scripts/topo-zoom-level-color-test.mjs +105 -0
  237. package/.next/static/chunks/0hndl9yzpqajt.css +0 -2
  238. package/.next/static/chunks/0jn4lbsj97vfl.js +0 -1
  239. package/.next/static/chunks/0rz3-6.xs78~d.js +0 -1
  240. package/.next/static/chunks/0~~xbw42y4m3v.js +0 -4
  241. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → IPCDbF7lRKC58UL3VvTa9}/_buildManifest.js +0 -0
  242. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → IPCDbF7lRKC58UL3VvTa9}/_clientMiddlewareManifest.js +0 -0
  243. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → IPCDbF7lRKC58UL3VvTa9}/_ssgManifest.js +0 -0
@@ -0,0 +1,98 @@
1
+ /* Round 522 verification: chrome Ring/Grid layout toggle's inactive
2
+ * variant gains `hover:font-medium` typography preview. Closes chrome
3
+ * toggle group typography hover-fw family at the last remaining toggle.
4
+ *
5
+ * Test phases:
6
+ * 1. default (layout=ring): Ring active fw=500, Grid inactive fw=400
7
+ * + Grid data-attr = '500' (preview target)
8
+ * + Ring data-attr = null
9
+ * 2. hover Grid (inactive): fontWeight → 500 (preview lands)
10
+ * 3. source-side regex confirms hover:font-medium + transition wired
11
+ * for both buttons
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
18
+
19
+ const browser = await chromium.launch({ headless: true });
20
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(() => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', 'ring');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } catch {}
28
+ });
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 mk = (alias) => ({
34
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
35
+ network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ });
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1')] } });
39
+ });
40
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
41
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
44
+ await page.waitForSelector('[data-topo-chrome-layout]', { timeout: 15000 });
45
+ await page.waitForTimeout(800);
46
+
47
+ const buttons = await page.evaluate(() => {
48
+ const els = Array.from(document.querySelectorAll('[data-topo-chrome-layout]'));
49
+ return els.map((el) => ({
50
+ label: el.getAttribute('data-topo-chrome-layout'),
51
+ active: el.getAttribute('data-topo-chrome-layout-active'),
52
+ previewAttr: el.getAttribute('data-topo-chrome-layout-hover-preview-fw'),
53
+ fw: getComputedStyle(el).fontWeight,
54
+ }));
55
+ });
56
+
57
+ // Phase 2: hover the inactive (Grid since layout=ring default)
58
+ await page.hover('[data-topo-chrome-layout="grid"]');
59
+ await page.waitForTimeout(350);
60
+ const gridHoverFw = await page.evaluate(() =>
61
+ getComputedStyle(document.querySelector('[data-topo-chrome-layout="grid"]')).fontWeight
62
+ );
63
+
64
+ await browser.close();
65
+
66
+ // Source regex
67
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
68
+ const sourceWiredClassRing = /'ring' \? '.*font-medium.*' : '.*hover:font-medium'.*\$\{chromePopping === 'layout-ring'/.test(src);
69
+ const sourceWiredClassGrid = /'grid' \? '.*font-medium.*' : '.*hover:font-medium'.*\$\{chromePopping === 'layout-grid'/.test(src);
70
+ const sourceWiredAttrRing = /data-topo-chrome-layout-hover-preview-fw=\{layout === 'ring' \? null : '500'\}/.test(src);
71
+ const sourceWiredAttrGrid = /data-topo-chrome-layout-hover-preview-fw=\{layout === 'grid' \? null : '500'\}/.test(src);
72
+ const sourceWiredTransRing = /transition: '.*font-weight 150ms ease'.*\n.*Ring/.test(src) ||
73
+ /font-weight 150ms ease'\s*\}\}\s*>\s*\n\s*Ring/.test(src);
74
+ const sourceWiredTransGrid = /borderColor:.*containerBorder.*font-weight 150ms ease/.test(src);
75
+
76
+ const ring = buttons.find(b => b.label === 'ring');
77
+ const grid = buttons.find(b => b.label === 'grid');
78
+
79
+ const results = {
80
+ ring_active_true: ring?.active === 'true',
81
+ ring_active_fw_500: ring?.fw === '500',
82
+ ring_no_preview_attr: ring?.previewAttr === null,
83
+ grid_active_false: grid?.active === 'false',
84
+ grid_inactive_fw_400: grid?.fw === '400',
85
+ grid_preview_attr_500: grid?.previewAttr === '500',
86
+ grid_hover_fw_500: gridHoverFw === '500',
87
+ source_class_ring: sourceWiredClassRing,
88
+ source_class_grid: sourceWiredClassGrid,
89
+ source_attr_ring: sourceWiredAttrRing,
90
+ source_attr_grid: sourceWiredAttrGrid,
91
+ source_trans_grid: sourceWiredTransGrid,
92
+ };
93
+ const ok = Object.values(results).every(Boolean);
94
+ console.log(`${ok ? '✅' : '❌'} R522 layout hover-fw preview:`,
95
+ JSON.stringify(results, null, 2),
96
+ '\n buttons:', JSON.stringify(buttons),
97
+ '\n grid hover fw:', gridHoverFw);
98
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,108 @@
1
+ /* Round 518 verification: legend-row COUNT digit gains the same 3-tier
2
+ * hover-letter-spacing tween R433 added to the LABEL — symmetric row
3
+ * hover gesture. Also lifts transition list 150 → 200ms closing R475
4
+ * panel-row cadence at the count surface.
5
+ *
6
+ * Test phases:
7
+ * 1. rest: letter-spacing 0px, attr='0px', transition includes '200ms'
8
+ * 2. hover: letter-spacing 0.25px, attr='0.25px'
9
+ * 3. source-side regex confirms 3-tier wiring + 200ms cadence
10
+ *
11
+ * Pin tier (0.5px) requires real click + filter-pin localStorage —
12
+ * skip and rely on source-regex for the pinned tier.
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try {
25
+ localStorage.setItem('anet-theme', 'cyber');
26
+ localStorage.setItem('anet-topo-layout', 'ring');
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } catch {}
29
+ });
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, status) => ({
35
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
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('a·1', 'idle'),
41
+ mk('a·2', 'idle'),
42
+ mk('b·1', 'working'),
43
+ ] } });
44
+ });
45
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
46
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-legend-count]', { timeout: 15000 });
50
+ await page.waitForTimeout(800);
51
+
52
+ // Pick the 'online' row's count (it'll have count > 0 so we see typography)
53
+ const targetSel = '[data-legend-count="idle"]';
54
+
55
+ // Phase 1: rest
56
+ const rest = await page.evaluate((sel) => {
57
+ const el = document.querySelector(sel);
58
+ if (!el) return null;
59
+ const cs = getComputedStyle(el);
60
+ return {
61
+ attrLS: el.getAttribute('data-legend-count-letter-spacing'),
62
+ attrTrans: el.getAttribute('data-legend-count-transition'),
63
+ letterSpacing: cs.letterSpacing,
64
+ transition: cs.transition,
65
+ };
66
+ }, targetSel);
67
+
68
+ // Phase 2: hover the row's hitbox (the parent g — but row hover is keyed
69
+ // on hoveredStatus which is set by mouseEnter on the row-hitbox rect.
70
+ // Easier: hover the LABEL text (also part of the row) and observe count.
71
+ await page.hover('[data-legend-row-label="idle"]');
72
+ await page.waitForTimeout(350);
73
+ const hover = await page.evaluate((sel) => {
74
+ const el = document.querySelector(sel);
75
+ if (!el) return null;
76
+ const cs = getComputedStyle(el);
77
+ return {
78
+ attrLS: el.getAttribute('data-legend-count-letter-spacing'),
79
+ letterSpacing: cs.letterSpacing,
80
+ };
81
+ }, targetSel);
82
+
83
+ await browser.close();
84
+
85
+ // Phase 3: source regex — verifies all 3 tiers + 200ms cadence
86
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
87
+ const sourceWiredLS =
88
+ /letterSpacing: isPinned \? '0\.5px' :\s*hoveredStatus === row\.key \? '0\.25px' : '0px',/.test(src);
89
+ const sourceWiredAttr =
90
+ /data-legend-count-letter-spacing=\{isPinned \? '0\.5px' : hoveredStatus === row\.key \? '0\.25px' : '0px'\}/.test(src);
91
+ const sourceWiredCadence =
92
+ /transition: 'opacity 200ms ease-out, fill 200ms ease-out, font-weight 200ms ease-out, letter-spacing 200ms ease-out',/.test(src);
93
+
94
+ const results = {
95
+ rest_attr_0: rest?.attrLS === '0px',
96
+ rest_attr_transition_200: rest?.attrTrans === '200ms',
97
+ rest_letter_spacing_0: rest?.letterSpacing === '0px' || rest?.letterSpacing === 'normal',
98
+ rest_transition_200ms: /(200ms|0\.2s)/.test(rest?.transition || ''),
99
+ hover_attr_025: hover?.attrLS === '0.25px',
100
+ hover_letter_spacing_025: hover?.letterSpacing === '0.25px',
101
+ source_3_tier_ls: sourceWiredLS,
102
+ source_attr_3_tier: sourceWiredAttr,
103
+ source_200ms_cadence: sourceWiredCadence,
104
+ };
105
+ const ok = Object.values(results).every(Boolean);
106
+ console.log(`${ok ? '✅' : '❌'} R518 legend-count 3-tier letter-spacing + 200ms:`, JSON.stringify(results, null, 2),
107
+ '\n rest:', JSON.stringify(rest), '\n hover:', JSON.stringify(hover));
108
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,107 @@
1
+ /* Round 531 verification: legend-row label gains fontWeight 500 → 600 on
2
+ * hover OR pin. Hover-fw family 8th anchor; symmetric to R530 recent-row.
3
+ *
4
+ * Test phases:
5
+ * 1. rest: attr='500', fw=500, hovered/pinned='false'
6
+ * 2. hover legend `idle` row label:
7
+ * attr='600', fw=600, hovered='true'
8
+ * 3. mouseleave: attr returns to '500'
9
+ * 4. source-side regex confirms ternary + transition list extension
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
16
+
17
+ const browser = await chromium.launch({ headless: true });
18
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try {
22
+ localStorage.setItem('anet-theme', 'cyber');
23
+ localStorage.setItem('anet-topo-layout', 'ring');
24
+ sessionStorage.setItem('anet_v3_auth', '1');
25
+ } catch {}
26
+ });
27
+ await ctx.route('**/api/hub/status*', async (route) => {
28
+ const r = await route.fetch();
29
+ const b = await r.json();
30
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
31
+ const mk = (alias, status) => ({
32
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
33
+ network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ });
36
+ // 3 idle, 1 working — legend rows have counts
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [
38
+ mk('a·1', 'idle'), mk('a·2', 'idle'), mk('a·3', 'idle'), mk('a·4', 'working'),
39
+ ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
42
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
43
+ const page = await ctx.newPage();
44
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
45
+ await page.waitForSelector('[data-legend-row-label="idle"]', { timeout: 15000 });
46
+ await page.waitForTimeout(800);
47
+
48
+ const sel = '[data-legend-row-label="idle"]';
49
+
50
+ const restRead = async () => page.evaluate((sel) => {
51
+ const el = document.querySelector(sel);
52
+ if (!el) return null;
53
+ const cs = getComputedStyle(el);
54
+ return {
55
+ attrFw: el.getAttribute('data-legend-row-label-font-weight'),
56
+ attrPinned: el.getAttribute('data-legend-row-label-pinned'),
57
+ attrHovered: el.getAttribute('data-legend-row-label-hovered'),
58
+ fontWeight: cs.fontWeight,
59
+ transition: cs.transition,
60
+ };
61
+ }, sel);
62
+
63
+ // Phase 1: rest
64
+ const rest = await restRead();
65
+
66
+ // Phase 2: hover the row label
67
+ await page.hover(sel);
68
+ await page.waitForTimeout(400);
69
+ const hover = await restRead();
70
+
71
+ // Phase 3: mouseleave
72
+ await page.mouse.move(50, 50);
73
+ await page.waitForTimeout(400);
74
+ const leave = await restRead();
75
+
76
+ await browser.close();
77
+
78
+ // Source regex
79
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
80
+ const sourceFwTernary =
81
+ /fontWeight=\{\(hoveredStatus === row\.key \|\| isPinned\) \? '600' : '500'\}/.test(src);
82
+ const sourceAttrWired =
83
+ /data-legend-row-label-font-weight=\{\(hoveredStatus === row\.key \|\| isPinned\) \? '600' : '500'\}/.test(src);
84
+ const sourceTransitionExt =
85
+ /transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out, font-weight 200ms ease-out'/.test(src);
86
+
87
+ const results = {
88
+ rest_attr_500: rest?.attrFw === '500',
89
+ rest_fw_500: rest?.fontWeight === '500',
90
+ rest_hovered_false: rest?.attrHovered === 'false',
91
+ rest_pinned_false: rest?.attrPinned === 'false',
92
+ hover_attr_600: hover?.attrFw === '600',
93
+ hover_fw_600: hover?.fontWeight === '600',
94
+ hover_hovered_true: hover?.attrHovered === 'true',
95
+ leave_attr_500: leave?.attrFw === '500',
96
+ leave_fw_500: leave?.fontWeight === '500',
97
+ source_fw_ternary: sourceFwTernary,
98
+ source_attr_wired: sourceAttrWired,
99
+ source_transition_ext: sourceTransitionExt,
100
+ };
101
+ const ok = Object.values(results).every(Boolean);
102
+ console.log(`${ok ? '✅' : '❌'} R531 legend-label hover-fw:`,
103
+ JSON.stringify(results, null, 2),
104
+ '\n rest:', JSON.stringify(rest),
105
+ '\n hover:', JSON.stringify(hover),
106
+ '\n leave:', JSON.stringify(leave));
107
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,102 @@
1
+ /* Round 569 verification: legend-row label gains drop-shadow glow
2
+ * on (hoveredStatus === row.key || isPinned). Mirror of R568
3
+ * recent-row text pattern at the sibling legend-row label scope.
4
+ * 18th anchor in drop-shadow family.
5
+ *
6
+ * Test phases:
7
+ * 1. rest: filter='none' on all 3 legend-row labels
8
+ * (working/idle/offline), glow-attr='false'
9
+ * 2. click pressure-seg working → pinnedStatus='working' →
10
+ * working legend-row label glow-attr='true', filter contains
11
+ * drop-shadow with cyan rgba
12
+ * 3. transition-property contains 'filter'
13
+ * 4. source-side regex confirms wiring
14
+ */
15
+ import { chromium } from 'playwright';
16
+ import { readFileSync } from 'node:fs';
17
+
18
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
19
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+
21
+ const browser = await chromium.launch({ headless: true });
22
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try {
26
+ localStorage.setItem('anet-theme', 'cyber');
27
+ localStorage.setItem('anet-topo-layout', 'ring');
28
+ sessionStorage.setItem('anet_v3_auth', '1');
29
+ } catch {}
30
+ });
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) => ({
36
+ alias, status, 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·1', 'working'),
42
+ mk('alpha·2', 'idle'),
43
+ mk('alpha·3', 'offline'),
44
+ ] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
47
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
48
+ const page = await ctx.newPage();
49
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
50
+ await page.waitForSelector('[data-legend-row-label="working"]', { timeout: 15000 });
51
+ await page.waitForTimeout(500);
52
+
53
+ const probe = (k) => page.evaluate((key) => {
54
+ const el = document.querySelector(`[data-legend-row-label="${key}"]`);
55
+ if (!el) return null;
56
+ const cs = getComputedStyle(el);
57
+ return {
58
+ filter: cs.filter,
59
+ transitionProperty: cs.transitionProperty,
60
+ glowAttr: el.getAttribute('data-legend-row-label-glow'),
61
+ pinnedAttr: el.getAttribute('data-legend-row-label-pinned'),
62
+ };
63
+ }, k);
64
+
65
+ const restWorking = await probe('working');
66
+ const restIdle = await probe('idle');
67
+ const restOffline = await probe('offline');
68
+
69
+ // Click pressure-seg working → sets pinnedStatus='working' →
70
+ // working legend-row label should glow.
71
+ await page.click('[data-pressure-seg="working"]');
72
+ await page.waitForTimeout(400);
73
+ const pinnedWorking = await probe('working');
74
+ const pinnedIdle = await probe('idle');
75
+
76
+ await browser.close();
77
+
78
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
79
+ const sourceFilter = /filter: \(hoveredStatus === row\.key \|\| isPinned\)\s*\?\s*`drop-shadow\(0 0 2px \$\{pal\.legendAccent\}80\)`\s*:\s*undefined/.test(src);
80
+ const sourceTransition = /transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out, font-weight 200ms ease-out, filter 200ms ease-out'/.test(src);
81
+ const sourceGlowAttr = /data-legend-row-label-glow=\{\(hoveredStatus === row\.key \|\| isPinned\) \? 'true' : 'false'\}/.test(src);
82
+
83
+ const results = {
84
+ rest_working_filter_none: restWorking?.filter === 'none',
85
+ rest_idle_filter_none: restIdle?.filter === 'none',
86
+ rest_offline_filter_none: restOffline?.filter === 'none',
87
+ rest_working_glow_false: restWorking?.glowAttr === 'false',
88
+ pinned_working_glow_true: pinnedWorking?.glowAttr === 'true',
89
+ pinned_working_filter: /drop-shadow\(/.test(pinnedWorking?.filter || ''),
90
+ pinned_idle_glow_false: pinnedIdle?.glowAttr === 'false',
91
+ transition_has_filter: /filter/.test(restWorking?.transitionProperty || ''),
92
+ source_filter: sourceFilter,
93
+ source_transition: sourceTransition,
94
+ source_glow_attr: sourceGlowAttr,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} R569 legend-row label drop-shadow glow (18th anchor, R568 sibling):`,
98
+ JSON.stringify(results, null, 2),
99
+ '\n rest working:', JSON.stringify(restWorking),
100
+ '\n pinned working:', JSON.stringify(pinnedWorking),
101
+ '\n pinned idle (unchanged):', JSON.stringify(pinnedIdle));
102
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,109 @@
1
+ /* Round 537 verification: legend swatch gains drop-shadow glow on
2
+ * hover/pin using its own row.fill color — drop-shadow family 13th
3
+ * anchor.
4
+ *
5
+ * Test phases:
6
+ * 1. rest: data-legend-swatch-glow='false', filter='none'
7
+ * 2. hover legend 'idle' row label (banked R518 path): swatch's glow
8
+ * attr='true', filter matches drop-shadow with the idle row fill
9
+ * (cyber #2dd4bf teal-400)
10
+ * 3. mouseleave: returns to 'false'/'none'
11
+ * 4. source-side regex confirms filter ternary + transition wiring
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
18
+
19
+ const browser = await chromium.launch({ headless: true });
20
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(() => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', 'ring');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } catch {}
28
+ });
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 mk = (alias, status) => ({
34
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
35
+ network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ });
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [
39
+ mk('a·1', 'idle'), mk('a·2', 'idle'), mk('a·3', 'working'),
40
+ ] } });
41
+ });
42
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
43
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
46
+ await page.waitForSelector('[data-legend-swatch="idle"]', { timeout: 15000 });
47
+ await page.waitForTimeout(800);
48
+
49
+ const sel = '[data-legend-swatch="idle"]';
50
+
51
+ const restRead = async () => page.evaluate((sel) => {
52
+ const el = document.querySelector(sel);
53
+ if (!el) return null;
54
+ const cs = getComputedStyle(el);
55
+ return {
56
+ glowAttr: el.getAttribute('data-legend-swatch-glow'),
57
+ state: el.getAttribute('data-legend-swatch-state'),
58
+ filter: cs.filter,
59
+ transition: cs.transition,
60
+ };
61
+ }, sel);
62
+
63
+ // Phase 1: rest
64
+ const rest = await restRead();
65
+
66
+ // Phase 2: hover the legend 'idle' row label — sibling row hover sets
67
+ // hoveredStatus='idle' which is the same gate as the swatch's hover.
68
+ await page.hover('[data-legend-row-label="idle"]');
69
+ await page.waitForTimeout(400);
70
+ const hover = await restRead();
71
+
72
+ // Phase 3: mouseleave
73
+ await page.mouse.move(50, 50);
74
+ await page.waitForTimeout(400);
75
+ const leave = await restRead();
76
+
77
+ await browser.close();
78
+
79
+ // Source regex
80
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
81
+ const sourceFilterTernary =
82
+ /filter: \(isRowHovered \|\| isPinned\)\s+\? `drop-shadow\(0 0 3px \$\{row\.fill\}99\)`\s+: undefined,/.test(src);
83
+ const sourceAttrWired =
84
+ /data-legend-swatch-glow=\{\(isRowHovered \|\| isPinned\) \? 'true' : 'false'\}/.test(src);
85
+ const sourceTransitionExt =
86
+ /transition: 'r 150ms ease-out, filter 150ms ease-out',/.test(src);
87
+
88
+ const results = {
89
+ rest_glow_false: rest?.glowAttr === 'false',
90
+ rest_state_idle: rest?.state === 'idle',
91
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
92
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
93
+ hover_glow_true: hover?.glowAttr === 'true',
94
+ hover_state_hover: hover?.state === 'hover',
95
+ hover_filter_drop_shadow: /drop-shadow/.test(hover?.filter || ''),
96
+ hover_filter_has_teal: /rgba?\(45,?\s*212,?\s*191/.test(hover?.filter || ''), // cyber idle teal-400 #2dd4bf (computed as rgba(...))
97
+ leave_glow_false: leave?.glowAttr === 'false',
98
+ leave_filter_none: leave?.filter === 'none' || leave?.filter === '',
99
+ source_filter_ternary: sourceFilterTernary,
100
+ source_attr_wired: sourceAttrWired,
101
+ source_transition_ext: sourceTransitionExt,
102
+ };
103
+ const ok = Object.values(results).every(Boolean);
104
+ console.log(`${ok ? '✅' : '❌'} R537 legend-swatch glow:`,
105
+ JSON.stringify(results, null, 2),
106
+ '\n rest:', JSON.stringify(rest),
107
+ '\n hover:', JSON.stringify(hover),
108
+ '\n leave:', JSON.stringify(leave));
109
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,139 @@
1
+ /* Round 562 verification: legend-row swatch lifts r + drop-shadow
2
+ * when operator hovers a node alias whose status matches the row's
3
+ * tier. 5th anchor in inspection-overrides-encoding family.
4
+ *
5
+ * Mock: alpha·1 (working) + alpha·2 (idle) + alpha·3 (offline).
6
+ * Hover alpha·1 → 'working' legend swatch should lift; idle +
7
+ * offline swatches stay at rest.
8
+ *
9
+ * Test phases:
10
+ * 1. rest: all 3 swatches r=6, filter=none, attr='false'
11
+ * 2. hover alpha·1 (working) → working swatch r=7, filter contains
12
+ * drop-shadow with green hex; idle + offline unchanged
13
+ * 3. hover alpha·2 (idle) → idle swatch lifts, others rest
14
+ * 4. hover alpha·3 (offline) → offline swatch lifts, others rest
15
+ * 5. source-side regex confirms wiring
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
21
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
22
+
23
+ const browser = await chromium.launch({ headless: true });
24
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try {
28
+ localStorage.setItem('anet-theme', 'cyber');
29
+ localStorage.setItem('anet-topo-layout', 'ring');
30
+ sessionStorage.setItem('anet_v3_auth', '1');
31
+ } catch {}
32
+ });
33
+ await ctx.route('**/api/hub/status*', async (route) => {
34
+ const r = await route.fetch();
35
+ const b = await r.json();
36
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
37
+ const mk = (alias, status) => ({
38
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
39
+ network_id: nid, project_dir: null,
40
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
41
+ });
42
+ await route.fulfill({ response: r, json: { ...b, sessions: [
43
+ mk('alpha·1', 'working'),
44
+ mk('alpha·2', 'idle'),
45
+ mk('alpha·3', 'offline'),
46
+ ] } });
47
+ });
48
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
52
+ await page.waitForSelector('[data-legend-swatch="working"]', { timeout: 15000 });
53
+ await page.waitForTimeout(500);
54
+
55
+ const probeSwatch = (key) => page.evaluate((k) => {
56
+ const el = document.querySelector(`[data-legend-swatch="${k}"]`);
57
+ if (!el) return null;
58
+ const cs = getComputedStyle(el);
59
+ return {
60
+ r: cs.r,
61
+ filter: cs.filter,
62
+ state: el.getAttribute('data-legend-swatch-state'),
63
+ matching: el.getAttribute('data-legend-swatch-member-alias-matching') === 'true',
64
+ glow: el.getAttribute('data-legend-swatch-glow') === 'true',
65
+ };
66
+ }, key);
67
+
68
+ const restWorking = await probeSwatch('working');
69
+ const restIdle = await probeSwatch('idle');
70
+ const restOffline = await probeSwatch('offline');
71
+
72
+ // Hover working node
73
+ await page.hover('g[data-node="alpha·1"]');
74
+ await page.waitForTimeout(400);
75
+ const hoverWorkingWorking = await probeSwatch('working');
76
+ const hoverWorkingIdle = await probeSwatch('idle');
77
+ const hoverWorkingOffline = await probeSwatch('offline');
78
+
79
+ // Move away to reset
80
+ await page.mouse.move(0, 0);
81
+ await page.waitForTimeout(300);
82
+
83
+ // Hover idle node
84
+ await page.hover('g[data-node="alpha·2"]');
85
+ await page.waitForTimeout(400);
86
+ const hoverIdleIdle = await probeSwatch('idle');
87
+
88
+ // Move away and hover offline node
89
+ await page.mouse.move(0, 0);
90
+ await page.waitForTimeout(300);
91
+ await page.hover('g[data-node="alpha·3"]');
92
+ await page.waitForTimeout(400);
93
+ const hoverOfflineOffline = await probeSwatch('offline');
94
+
95
+ await browser.close();
96
+
97
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
98
+ const sourceFlag = /const isMemberAliasMatching = hoveredAliasRowKey === row\.key;/.test(src);
99
+ const sourceLifted = /const isSwatchLifted = isLifted \|\| isMemberAliasMatching;/.test(src);
100
+ const sourceR = /r: isSwatchLifted \? '7px' : '6px'/.test(src);
101
+ const sourceFilter = /filter: isSwatchLifted/.test(src);
102
+ const sourceAttr = /data-legend-swatch-member-alias-matching=/.test(src);
103
+
104
+ const results = {
105
+ // Rest state — all swatches r=6, no glow
106
+ rest_working_r_6: restWorking?.r === '6px',
107
+ rest_working_filter_none: restWorking?.filter === 'none',
108
+ rest_working_matching_false: restWorking?.matching === false,
109
+ rest_idle_r_6: restIdle?.r === '6px',
110
+ rest_offline_r_6: restOffline?.r === '6px',
111
+ // Hover working node → working swatch lifts; idle + offline stay
112
+ hover_working_w_r_7: hoverWorkingWorking?.r === '7px',
113
+ hover_working_w_matching: hoverWorkingWorking?.matching === true,
114
+ hover_working_w_glow: /drop-shadow\(/.test(hoverWorkingWorking?.filter || ''),
115
+ hover_working_i_r_6: hoverWorkingIdle?.r === '6px',
116
+ hover_working_o_r_6: hoverWorkingOffline?.r === '6px',
117
+ // Hover idle node → idle swatch lifts
118
+ hover_idle_i_r_7: hoverIdleIdle?.r === '7px',
119
+ hover_idle_i_matching: hoverIdleIdle?.matching === true,
120
+ // Hover offline node → offline swatch lifts
121
+ hover_offline_o_r_7: hoverOfflineOffline?.r === '7px',
122
+ hover_offline_o_matching: hoverOfflineOffline?.matching === true,
123
+ // Source
124
+ source_flag: sourceFlag,
125
+ source_lifted: sourceLifted,
126
+ source_r: sourceR,
127
+ source_filter: sourceFilter,
128
+ source_attr: sourceAttr,
129
+ };
130
+ const ok = Object.values(results).every(Boolean);
131
+ console.log(`${ok ? '✅' : '❌'} R562 legend-swatch inspection-overrides-encoding (5th anchor):`,
132
+ JSON.stringify(results, null, 2),
133
+ '\n rest working:', JSON.stringify(restWorking),
134
+ '\n rest idle:', JSON.stringify(restIdle),
135
+ '\n rest offline:', JSON.stringify(restOffline),
136
+ '\n hover alpha·1 (working) → working:', JSON.stringify(hoverWorkingWorking),
137
+ '\n hover alpha·2 (idle) → idle:', JSON.stringify(hoverIdleIdle),
138
+ '\n hover alpha·3 (offline) → offline:', JSON.stringify(hoverOfflineOffline));
139
+ process.exit(ok ? 0 : 1);