@sleep2agi/agent-network-dashboard 0.5.3-preview.7 → 0.5.3-preview.70

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 (225) 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 +3 -3
  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/0boiij6s6nrkh.js +1 -0
  145. package/.next/static/chunks/0ehw4_lp248_1.js +1 -0
  146. package/.next/static/chunks/0ej-5z4i~xbwe.css +2 -0
  147. package/.next/static/chunks/{0jz0q_~y452m5.js → 0mkpm4lv6985f.js} +1 -1
  148. package/.next/static/chunks/0qwcmg950z6u5.js +4 -0
  149. package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
  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 +1910 -86
  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-brand-logo-breath-test.mjs +102 -0
  161. package/scripts/topo-brand-logo-hover-rotate-test.mjs +93 -0
  162. package/scripts/topo-brand-logo-hover-test.mjs +85 -0
  163. package/scripts/topo-chip-row-digit-ls-test.mjs +135 -0
  164. package/scripts/topo-cluster-count-attr-test.mjs +80 -0
  165. package/scripts/topo-crescent-breath-test.mjs +104 -0
  166. package/scripts/topo-crescent-recede-test.mjs +111 -0
  167. package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
  168. package/scripts/topo-edge-pill-glow-test.mjs +67 -0
  169. package/scripts/topo-filter-pill-glow-test.mjs +90 -0
  170. package/scripts/topo-filter-pills-press-test.mjs +96 -0
  171. package/scripts/topo-fleet-density-tier-test.mjs +84 -0
  172. package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
  173. package/scripts/topo-fullscreen-attr-test.mjs +73 -0
  174. package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
  175. package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
  176. package/scripts/topo-group-pill-glow-test.mjs +76 -0
  177. package/scripts/topo-hub-digit-ls-test.mjs +119 -0
  178. package/scripts/topo-hub-halo-glow-test.mjs +96 -0
  179. package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
  180. package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
  181. package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
  182. package/scripts/topo-hub-highlight-r-test.mjs +112 -0
  183. package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
  184. package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
  185. package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
  186. package/scripts/topo-hub-idle-breath-test.mjs +109 -0
  187. package/scripts/topo-hub-recede-test.mjs +124 -0
  188. package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
  189. package/scripts/topo-layout-hover-fw-test.mjs +98 -0
  190. package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
  191. package/scripts/topo-legend-label-fw-test.mjs +107 -0
  192. package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
  193. package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
  194. package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
  195. package/scripts/topo-orphan-box-dash-test.mjs +89 -0
  196. package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
  197. package/scripts/topo-orphan-label-italic-test.mjs +90 -0
  198. package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
  199. package/scripts/topo-panel-title-glow-test.mjs +111 -0
  200. package/scripts/topo-pill-x-rotate-test.mjs +96 -0
  201. package/scripts/topo-pinned-aspect-test.mjs +89 -0
  202. package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
  203. package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
  204. package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
  205. package/scripts/topo-recent-more-fw-test.mjs +126 -0
  206. package/scripts/topo-recent-row-fw-test.mjs +115 -0
  207. package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
  208. package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
  209. package/scripts/topo-starfield-hue-test.mjs +109 -0
  210. package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
  211. package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
  212. package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
  213. package/scripts/topo-vendor-activelinks-press-test.mjs +100 -0
  214. package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
  215. package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
  216. package/scripts/topo-watermark-breath-test.mjs +100 -0
  217. package/scripts/topo-watermark-recede-test.mjs +114 -0
  218. package/scripts/topo-zoom-level-color-test.mjs +105 -0
  219. package/.next/static/chunks/0hndl9yzpqajt.css +0 -2
  220. package/.next/static/chunks/0ia4_98ytam_7.js +0 -1
  221. package/.next/static/chunks/0md~c._ox5owe.js +0 -4
  222. package/.next/static/chunks/15w33hhbf3.28.js +0 -1
  223. /package/.next/static/{bIxZRyo8qjN2ZGvJ_1dmh → 8fEL_YAK7U48berKDXWZQ}/_buildManifest.js +0 -0
  224. /package/.next/static/{bIxZRyo8qjN2ZGvJ_1dmh → 8fEL_YAK7U48berKDXWZQ}/_clientMiddlewareManifest.js +0 -0
  225. /package/.next/static/{bIxZRyo8qjN2ZGvJ_1dmh → 8fEL_YAK7U48berKDXWZQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,84 @@
1
+ /* Round 510 verification: hub-highlight transition list extends to
2
+ * include `fill 200ms ease-out` alongside existing `opacity 300ms`.
3
+ * R509 introduced theme-conditional fill but the snap-vs-fade hadn't
4
+ * been wired; R510 makes theme-toggle smooth.
5
+ *
6
+ * Verifies:
7
+ * 1. computed transition-property includes both 'fill' and 'opacity'
8
+ * 2. computed transition-duration shows 200ms and 300ms
9
+ * 3. source-side regex confirms the new spec
10
+ * 4. R509 theme-conditional fill still works (cross-theme differential)
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ async function probe(theme) {
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((t) => {
23
+ try {
24
+ localStorage.setItem('anet-theme', t);
25
+ localStorage.setItem('anet-topo-layout', 'ring');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } catch {}
28
+ }, theme);
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: [mk('a·1', 'idle')] } });
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-hub-highlight]', { timeout: 15000 });
45
+ await page.waitForTimeout(1500);
46
+ const result = await page.evaluate(() => {
47
+ const circle = document.querySelector('[data-topo-hub-highlight]');
48
+ if (!circle) return null;
49
+ const cs = window.getComputedStyle(circle);
50
+ return {
51
+ fill_attr: circle.getAttribute('fill'),
52
+ transition_property: cs.transitionProperty,
53
+ transition_duration: cs.transitionDuration,
54
+ };
55
+ });
56
+ await browser.close();
57
+ return result;
58
+ }
59
+
60
+ const cyber = await probe('cyber');
61
+ const light = await probe('light');
62
+
63
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
64
+ const sourceWired = /transition: 'opacity 300ms ease-out, fill 200ms ease-out'/.test(src);
65
+
66
+ const tpHas = (s, prop) => new RegExp(`\\b${prop}\\b`, 'i').test(s || '');
67
+ const durHas = (s, ms) => (s || '').split(',').map((x) => x.trim()).includes(ms);
68
+
69
+ const results = {
70
+ cyber_tp_has_fill: cyber && tpHas(cyber.transition_property, 'fill'),
71
+ cyber_tp_has_opacity: cyber && tpHas(cyber.transition_property, 'opacity'),
72
+ cyber_dur_200ms: cyber && durHas(cyber.transition_duration, '0.2s'),
73
+ cyber_dur_300ms: cyber && durHas(cyber.transition_duration, '0.3s'),
74
+ light_tp_has_fill: light && tpHas(light.transition_property, 'fill'),
75
+ light_tp_has_opacity: light && tpHas(light.transition_property, 'opacity'),
76
+ cyber_fill_d1fae5: cyber && cyber.fill_attr === '#d1fae5',
77
+ light_fill_10b981: light && light.fill_attr === '#10b981',
78
+ source_wired: sourceWired,
79
+ };
80
+ const ok = Object.values(results).every(Boolean);
81
+ console.log(`${ok ? '✅' : '❌'} R510 hub-highlight fill transition:`, JSON.stringify(results),
82
+ '\n cyber:', JSON.stringify(cyber),
83
+ '\n light:', JSON.stringify(light));
84
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,99 @@
1
+ /* Round 532 verification: hub-highlight disc gains drop-shadow glow on
2
+ * hub-hover — drop-shadow visual-polish family 8th anchor.
3
+ *
4
+ * Test phases (workingCount === 0 so highlight is visible):
5
+ * 1. rest: data-topo-hub-highlight-glow='false', computed filter='none'
6
+ * 2. hover (page.mouse.move to highlight bbox per R527 banked path):
7
+ * glow='true', computed filter matches 'drop-shadow(...)' with
8
+ * cyber emerald-400 hue
9
+ * 3. transition list includes 'filter 200ms ease-out'
10
+ * 4. source-side regex confirms filter ternary + transition list wiring
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ const browser = await chromium.launch({ headless: true });
19
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [
38
+ mk('a·1'), mk('a·2'), mk('a·3'),
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-topo-hub-highlight]', { timeout: 15000 });
46
+ await page.waitForTimeout(800);
47
+
48
+ const restRead = async () => page.evaluate(() => {
49
+ const el = document.querySelector('[data-topo-hub-highlight]');
50
+ if (!el) return null;
51
+ const cs = getComputedStyle(el);
52
+ return {
53
+ attrGlow: el.getAttribute('data-topo-hub-highlight-glow'),
54
+ attrHovered: el.getAttribute('data-topo-hub-highlight-hovered'),
55
+ filter: cs.filter,
56
+ transition: cs.transition,
57
+ };
58
+ });
59
+
60
+ // Phase 1: rest
61
+ const rest = await restRead();
62
+
63
+ // Phase 2: hover hub via mouse.move to highlight bbox center
64
+ const bbox = await page.locator('[data-topo-hub-highlight]').first().boundingBox();
65
+ if (bbox) {
66
+ await page.mouse.move(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2);
67
+ }
68
+ await page.waitForTimeout(400);
69
+ const hover = await restRead();
70
+
71
+ await browser.close();
72
+
73
+ // Source regex
74
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
75
+ const sourceFilterTernary =
76
+ /filter: !reducedMotion && hoveredHub\s+\? \(isLight\s+\? 'drop-shadow\(0 0 2px rgba\(16, 185, 129, 0\.6\)\)'\s+: 'drop-shadow\(0 0 3px rgba\(52, 211, 153, 0\.6\)\)'\)\s+: undefined,/.test(src);
77
+ const sourceAttrWired =
78
+ /data-topo-hub-highlight-glow=\{!reducedMotion && hoveredHub \? 'true' : 'false'\}/.test(src);
79
+ const sourceTransitionExt =
80
+ /transition: 'opacity 300ms ease-out, fill 200ms ease-out, r 200ms ease-out, filter 200ms ease-out'/.test(src);
81
+
82
+ const results = {
83
+ rest_glow_false: rest?.attrGlow === 'false',
84
+ rest_hovered_false: rest?.attrHovered === 'false',
85
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
86
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
87
+ hover_glow_true: hover?.attrGlow === 'true',
88
+ hover_hovered_true: hover?.attrHovered === 'true',
89
+ hover_filter_drop_shadow: /drop-shadow\(.+52,?\s*211,?\s*153/.test(hover?.filter || ''), // cyber emerald-400
90
+ source_filter_ternary: sourceFilterTernary,
91
+ source_attr_wired: sourceAttrWired,
92
+ source_transition_ext: sourceTransitionExt,
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} R532 hub-highlight hover-glow:`,
96
+ JSON.stringify(results, null, 2),
97
+ '\n rest:', JSON.stringify(rest),
98
+ '\n hover:', JSON.stringify(hover));
99
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,112 @@
1
+ /* Round 529 verification: hub-highlight gains geometric amplify
2
+ * r 5.5 → 6 on hub-hover — focal-amplify family 3rd anchor.
3
+ *
4
+ * Test phases (workingCount === 0 so highlight renders visibly):
5
+ * 1. rest: data-topo-hub-highlight-radius = 5.5,
6
+ * computed r ≈ 5.5px (CSS property),
7
+ * hovered attr = 'false'
8
+ * 2. hover hub (via page.mouse.move to highlight bbox):
9
+ * data-topo-hub-highlight-radius = 6,
10
+ * computed r ≈ 6px,
11
+ * hovered attr = 'true'
12
+ * 3. transition list includes `r 200ms ease-out`
13
+ * 4. source-side regex confirms highlightR ternary + style.r 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) => ({
36
+ alias, status: 'idle', 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
+ // workingCount=0 (all idle) so highlight is visible
41
+ await route.fulfill({ response: r, json: { ...b, sessions: [
42
+ mk('alpha·1'), mk('alpha·2'), mk('alpha·3'),
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-topo-hub-highlight]', { timeout: 15000 });
50
+ await page.waitForTimeout(800);
51
+
52
+ const restRead = async () => page.evaluate(() => {
53
+ const el = document.querySelector('[data-topo-hub-highlight]');
54
+ if (!el) return null;
55
+ const cs = getComputedStyle(el);
56
+ return {
57
+ attrR: el.getAttribute('data-topo-hub-highlight-radius'),
58
+ attrHovered: el.getAttribute('data-topo-hub-highlight-hovered'),
59
+ attrVisible: el.getAttribute('data-topo-hub-highlight-visible'),
60
+ cssR: cs.r, // computed CSS `r` value
61
+ transition: cs.transition,
62
+ };
63
+ });
64
+
65
+ // Phase 1: rest
66
+ const rest = await restRead();
67
+
68
+ // Phase 2: hover hub (via mouse.move to highlight bbox center —
69
+ // banked path from R527 hub-digit-ls test)
70
+ const bbox = await page.locator('[data-topo-hub-highlight]').first().boundingBox();
71
+ if (bbox) {
72
+ await page.mouse.move(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2);
73
+ }
74
+ await page.waitForTimeout(400);
75
+ const hover = 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 sourceHighlightRTernary =
82
+ /const highlightR = !reducedMotion && hoveredHub \? 6 : 5\.5;/.test(src);
83
+ const sourceStyleR =
84
+ /r: `\$\{highlightR\}px`,/.test(src);
85
+ const sourceTransitionR =
86
+ /transition: 'opacity 300ms ease-out, fill 200ms ease-out, r 200ms ease-out'/.test(src);
87
+ const sourceAttrDynamic =
88
+ /data-topo-hub-highlight-radius=\{highlightR\}/.test(src);
89
+
90
+ const approxEq = (a, b, tol = 0.1) => Math.abs(a - b) < tol;
91
+
92
+ const results = {
93
+ rest_attr_55: rest?.attrR === '5.5',
94
+ rest_hovered_false: rest?.attrHovered === 'false',
95
+ rest_visible_true: rest?.attrVisible === 'true', // workingCount=0
96
+ rest_css_r_55: approxEq(parseFloat(rest?.cssR || '0'), 5.5),
97
+ rest_transition_has_r: /\br\b/.test(rest?.transition || '') ||
98
+ /\b(r 200ms|r 0\.2s)/.test(rest?.transition || ''),
99
+ hover_attr_6: hover?.attrR === '6',
100
+ hover_hovered_true: hover?.attrHovered === 'true',
101
+ hover_css_r_6: approxEq(parseFloat(hover?.cssR || '0'), 6),
102
+ source_highlightR_ternary: sourceHighlightRTernary,
103
+ source_style_r: sourceStyleR,
104
+ source_transition_r: sourceTransitionR,
105
+ source_attr_dynamic: sourceAttrDynamic,
106
+ };
107
+ const ok = Object.values(results).every(Boolean);
108
+ console.log(`${ok ? '✅' : '❌'} R529 hub-highlight hover r:`,
109
+ JSON.stringify(results, null, 2),
110
+ '\n rest:', JSON.stringify(rest),
111
+ '\n hover:', JSON.stringify(hover));
112
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,144 @@
1
+ /* Round 508 verification: hub-highlight circle joins the focal-recede
2
+ * pattern (2nd anchor after R507's hub digit). When canvas attention
3
+ * is elsewhere, the hub focal cluster (digit + highlight) recedes as
4
+ * a unit:
5
+ * - hub-digit opacity: 1 → 0.85 (R507)
6
+ * - hub-highlight opacity: 0.95 → 0.81 (R508, 0.95 × 0.85)
7
+ * - hub-highlight SMIL breath: active → halted (R508)
8
+ *
9
+ * Also verifies R497 idle-breath still works in the un-receded state
10
+ * — refactor regression check.
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ const browser = await chromium.launch({ headless: true });
19
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias, status) => ({
33
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ // workingCount === 0 (all idle) so hub-highlight is visible
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [
39
+ mk('alpha·a1', 'idle'),
40
+ mk('alpha·a2', 'idle'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
47
+ await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
48
+ await page.waitForTimeout(1500);
49
+
50
+ // Phase 1: rest state — idle fleet, no external hover
51
+ // expected: opacity 0.95, breath='true', recede='false', animate present
52
+ const rest = await page.evaluate(() => {
53
+ const circle = document.querySelector('[data-topo-hub-highlight]');
54
+ if (!circle) return null;
55
+ return {
56
+ opacity_attr: circle.getAttribute('data-topo-hub-highlight-opacity'),
57
+ breath_attr: circle.getAttribute('data-topo-hub-highlight-breath'),
58
+ recede_attr: circle.getAttribute('data-topo-hub-highlight-recede'),
59
+ has_animate: !!circle.querySelector('animate[attributeName="opacity"]'),
60
+ };
61
+ });
62
+
63
+ // Phase 2: external hover via R488 synthetic dispatch on a node
64
+ // expected: opacity 0.81 (0.95 * 0.85), breath='false', recede='true',
65
+ // animate ABSENT (halted)
66
+ await page.evaluate(() => {
67
+ const g = document.querySelector('g[data-node]');
68
+ if (!g) return;
69
+ const target = g.querySelector('circle, image, rect') || g;
70
+ ['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
71
+ target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
72
+ });
73
+ });
74
+ await page.waitForTimeout(500);
75
+ const hover = await page.evaluate(() => {
76
+ const circle = document.querySelector('[data-topo-hub-highlight]');
77
+ if (!circle) return null;
78
+ return {
79
+ opacity_attr: circle.getAttribute('data-topo-hub-highlight-opacity'),
80
+ breath_attr: circle.getAttribute('data-topo-hub-highlight-breath'),
81
+ recede_attr: circle.getAttribute('data-topo-hub-highlight-recede'),
82
+ has_animate: !!circle.querySelector('animate[attributeName="opacity"]'),
83
+ };
84
+ });
85
+
86
+ // Phase 3: release hover
87
+ await page.evaluate(() => {
88
+ const g = document.querySelector('g[data-node]');
89
+ if (!g) return;
90
+ const target = g.querySelector('circle, image, rect') || g;
91
+ ['pointerleave', 'pointerout', 'mouseleave', 'mouseout'].forEach((t) => {
92
+ target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
93
+ });
94
+ });
95
+ await page.waitForTimeout(500);
96
+ const release = await page.evaluate(() => {
97
+ const circle = document.querySelector('[data-topo-hub-highlight]');
98
+ if (!circle) return null;
99
+ return {
100
+ opacity_attr: circle.getAttribute('data-topo-hub-highlight-opacity'),
101
+ breath_attr: circle.getAttribute('data-topo-hub-highlight-breath'),
102
+ recede_attr: circle.getAttribute('data-topo-hub-highlight-recede'),
103
+ has_animate: !!circle.querySelector('animate[attributeName="opacity"]'),
104
+ };
105
+ });
106
+
107
+ await browser.close();
108
+
109
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
110
+ const sourceIIFE = /const hubRecede = !!\(\(hoveredAlias \|\| hoveredEdgeKey \|\| hoveredGroupLabel/.test(src);
111
+ const sourceOpacityMultiplier = /const resolvedOpacity = hubRecede \? baseOpacity \* 0\.85 : baseOpacity;/.test(src);
112
+ // R511 extension: breathActive now also halts on hoveredHub (hub-hover
113
+ // amplify state added to the 3-state opacity ladder). Regex updated to
114
+ // match the extended formula. Runtime DOM contract (rest = breath true)
115
+ // unchanged.
116
+ const sourceBreathGate = /const breathActive = !reducedMotion && workingCount === 0 && !hubRecede && !hoveredHub;/.test(src);
117
+ const sourceAnimateGate = /\{breathActive && \(\s*<animate/.test(src);
118
+
119
+ const approxEq = (a, b) => Math.abs(parseFloat(a) - b) < 0.01;
120
+
121
+ const results = {
122
+ rest_opacity_095: rest && approxEq(rest.opacity_attr, 0.95),
123
+ rest_breath_true: rest && rest.breath_attr === 'true',
124
+ rest_recede_false: rest && rest.recede_attr === 'false',
125
+ rest_has_animate: rest && rest.has_animate,
126
+ hover_opacity_081: hover && approxEq(hover.opacity_attr, 0.81),
127
+ hover_breath_false: hover && hover.breath_attr === 'false',
128
+ hover_recede_true: hover && hover.recede_attr === 'true',
129
+ hover_no_animate: hover && !hover.has_animate,
130
+ release_opacity_095: release && approxEq(release.opacity_attr, 0.95),
131
+ release_breath_true: release && release.breath_attr === 'true',
132
+ release_recede_false:release && release.recede_attr === 'false',
133
+ release_has_animate: release && release.has_animate,
134
+ source_iife_wired: sourceIIFE,
135
+ source_multiplier_wired: sourceOpacityMultiplier,
136
+ source_breath_gate_wired: sourceBreathGate,
137
+ source_animate_gate_wired:sourceAnimateGate,
138
+ };
139
+ const ok = Object.values(results).every(Boolean);
140
+ console.log(`${ok ? '✅' : '❌'} R508 hub-highlight recede:`, JSON.stringify(results),
141
+ '\n rest:', JSON.stringify(rest),
142
+ '\n hover:', JSON.stringify(hover),
143
+ '\n release:', JSON.stringify(release));
144
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,83 @@
1
+ /* Round 509 verification: hub-highlight circle fill switches per theme.
2
+ * dark/cyber theme: #d1fae5 (emerald-100, pale)
3
+ * light theme: #10b981 (emerald-600, vibrant)
4
+ *
5
+ * Fixes a light-theme contrast issue where pale emerald-100 on a
6
+ * pale background rendered the dim disc effectively invisible.
7
+ *
8
+ * Test scenarios:
9
+ * 1. cyber theme — fill resolves to #d1fae5 (or rgb equivalent)
10
+ * 2. light theme — fill resolves to #10b981 (or rgb equivalent)
11
+ * 3. source-side ternary wired
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
+ async function probe(theme) {
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((t) => {
24
+ try {
25
+ localStorage.setItem('anet-theme', t);
26
+ localStorage.setItem('anet-topo-layout', 'ring');
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } catch {}
29
+ }, theme);
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
+ // idle fleet so hub-highlight is visible
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('alpha·a1', 'idle'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
49
+ await page.waitForTimeout(1500);
50
+ const result = await page.evaluate(() => {
51
+ const circle = document.querySelector('[data-topo-hub-highlight]');
52
+ if (!circle) return null;
53
+ return {
54
+ fill_attr: circle.getAttribute('fill'),
55
+ computed_fill: window.getComputedStyle(circle).fill,
56
+ };
57
+ });
58
+ await browser.close();
59
+ return result;
60
+ }
61
+
62
+ const cyber = await probe('cyber');
63
+ const light = await probe('light');
64
+
65
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
66
+ const sourceWired = /fill=\{isLight \? '#10b981' : '#d1fae5'\}/.test(src);
67
+
68
+ // fill_attr is the actual SVG attribute value (literal '#d1fae5' or '#10b981')
69
+ // computed_fill is browser-normalized ('rgb(...)' typically)
70
+ const cyberHexMatch = cyber && cyber.fill_attr === '#d1fae5';
71
+ const lightHexMatch = light && light.fill_attr === '#10b981';
72
+
73
+ const results = {
74
+ cyber_fill_d1fae5: cyberHexMatch,
75
+ light_fill_10b981: lightHexMatch,
76
+ themes_differ: cyber && light && cyber.fill_attr !== light.fill_attr,
77
+ source_wired: sourceWired,
78
+ };
79
+ const ok = Object.values(results).every(Boolean);
80
+ console.log(`${ok ? '✅' : '❌'} R509 hub-highlight theme fill:`, JSON.stringify(results),
81
+ '\n cyber:', JSON.stringify(cyber),
82
+ '\n light:', JSON.stringify(light));
83
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,97 @@
1
+ /* Round 535 verification: hub hover-ring gains drop-shadow glow on
2
+ * hub-hover — completes hub-cluster glow quartet (digit + disc +
3
+ * spokes + ring).
4
+ *
5
+ * Test phases:
6
+ * 1. rest: glow attr='false', filter='none', opacity=0
7
+ * 2. hover hub (mouse.move to hub center bbox): glow='true',
8
+ * filter has cyber emerald-400 drop-shadow, opacity > 0
9
+ * 3. transition list includes 'filter 200ms ease-out'
10
+ * 4. source-side regex confirms filter ternary + transition wiring
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ const browser = await chromium.launch({ headless: true });
19
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
43
+ await page.waitForSelector('[data-topo-hub-hover-ring]', { timeout: 15000 });
44
+ await page.waitForTimeout(800);
45
+
46
+ const restRead = async () => page.evaluate(() => {
47
+ const el = document.querySelector('[data-topo-hub-hover-ring]');
48
+ if (!el) return null;
49
+ const cs = getComputedStyle(el);
50
+ return {
51
+ glowAttr: el.getAttribute('data-topo-hub-hover-ring-glow'),
52
+ opacity: el.getAttribute('opacity'),
53
+ filter: cs.filter,
54
+ transition: cs.transition,
55
+ };
56
+ });
57
+
58
+ // Phase 1: rest
59
+ const rest = await restRead();
60
+
61
+ // Phase 2: hover hub via hub-highlight bbox (banked R527 path)
62
+ const hubBbox = await page.locator('[data-topo-hub-highlight]').first().boundingBox();
63
+ if (hubBbox) {
64
+ await page.mouse.move(hubBbox.x + hubBbox.width / 2, hubBbox.y + hubBbox.height / 2);
65
+ }
66
+ await page.waitForTimeout(400);
67
+ const hover = await restRead();
68
+
69
+ await browser.close();
70
+
71
+ // Source regex
72
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
73
+ const sourceFilterTernary =
74
+ /filter: !reducedMotion && hoveredHub\s+\? \(isLight\s+\? 'drop-shadow\(0 0 3px rgba\(16, 185, 129, 0\.5\)\)'\s+: 'drop-shadow\(0 0 3px rgba\(52, 211, 153, 0\.5\)\)'\)\s+: undefined,/.test(src);
75
+ const sourceAttrWired =
76
+ /data-topo-hub-hover-ring-glow=\{!reducedMotion && hoveredHub \? 'true' : 'false'\}/.test(src);
77
+ const sourceTransitionExt =
78
+ /transition: 'opacity 180ms ease-out, r 180ms ease-out, stroke 200ms ease-out, filter 200ms ease-out'/.test(src);
79
+
80
+ const results = {
81
+ rest_glow_false: rest?.glowAttr === 'false',
82
+ rest_opacity_0: rest?.opacity === '0',
83
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
84
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
85
+ hover_glow_true: hover?.glowAttr === 'true',
86
+ hover_opacity_visible: parseFloat(hover?.opacity || '0') > 0,
87
+ hover_filter_drop_shadow: /drop-shadow\(.+52,?\s*211,?\s*153/.test(hover?.filter || ''), // cyber emerald-400
88
+ source_filter_ternary: sourceFilterTernary,
89
+ source_attr_wired: sourceAttrWired,
90
+ source_transition_ext: sourceTransitionExt,
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} R535 hub-hover-ring glow:`,
94
+ JSON.stringify(results, null, 2),
95
+ '\n rest:', JSON.stringify(rest),
96
+ '\n hover:', JSON.stringify(hover));
97
+ process.exit(ok ? 0 : 1);