@sleep2agi/agent-network-dashboard 0.5.2-preview.1 → 0.5.2-preview.10

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 (153) 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 +7 -7
  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.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/admin.html +1 -1
  21. package/.next/server/app/admin.rsc +1 -1
  22. package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
  23. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
  25. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  28. package/.next/server/app/index.html +2 -2
  29. package/.next/server/app/index.rsc +2 -2
  30. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  32. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/login.html +2 -2
  37. package/.next/server/app/login.rsc +2 -2
  38. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  39. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/login.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  43. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  44. package/.next/server/app/logs.html +1 -1
  45. package/.next/server/app/logs.rsc +1 -1
  46. package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
  47. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  48. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  49. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  50. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  51. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  52. package/.next/server/app/messages.html +1 -1
  53. package/.next/server/app/messages.rsc +1 -1
  54. package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
  55. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  59. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  60. package/.next/server/app/node.html +1 -1
  61. package/.next/server/app/node.rsc +1 -1
  62. package/.next/server/app/node.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/node.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  68. package/.next/server/app/nodes.html +1 -1
  69. package/.next/server/app/nodes.rsc +1 -1
  70. package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  76. package/.next/server/app/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/server-logs.html +1 -1
  78. package/.next/server/app/server-logs.rsc +1 -1
  79. package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
  80. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  81. package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
  82. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  83. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  84. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  85. package/.next/server/app/settings/networks.html +1 -1
  86. package/.next/server/app/settings/networks.rsc +1 -1
  87. package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
  88. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  89. package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
  90. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  92. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  93. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  94. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/tokens.html +1 -1
  96. package/.next/server/app/settings/tokens.rsc +1 -1
  97. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
  98. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
  100. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  101. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  103. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings.html +2 -2
  105. package/.next/server/app/settings.rsc +2 -2
  106. package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  107. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  108. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  109. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  111. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  112. package/.next/server/app/tasks.html +1 -1
  113. package/.next/server/app/tasks.rsc +1 -1
  114. package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
  115. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  116. package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
  117. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  118. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  119. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  120. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  121. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
  123. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  126. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  127. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  128. package/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/server/pages/404.html +1 -1
  130. package/.next/server/pages/500.html +1 -1
  131. package/.next/static/chunks/0tio1iob9rlrz.js +1 -0
  132. package/.next/static/chunks/0xkn4hstf40ax.js +1 -0
  133. package/.next/static/chunks/{0pgnr.wdna.jv.js → 0z3uxdpbxx6vi.js} +1 -1
  134. package/.next/static/chunks/0z_jx0znc~hgn.js +4 -0
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +208 -9
  138. package/package.json +1 -1
  139. package/scripts/topo-group-label-glow-test.mjs +104 -0
  140. package/scripts/topo-hub-digit-glow-test.mjs +105 -0
  141. package/scripts/topo-layout-theme-attrs-test.mjs +91 -0
  142. package/scripts/topo-legend-pin-ring-glow-test.mjs +105 -0
  143. package/scripts/topo-legend-row-text-cadence-test.mjs +93 -0
  144. package/scripts/topo-legend-row-tint-cadence-test.mjs +92 -0
  145. package/scripts/topo-recent-row-freshness-glow-test.mjs +100 -0
  146. package/scripts/topo-recent-row-text-cadence-test.mjs +99 -0
  147. package/scripts/topo-recent-row-tint-cadence-test.mjs +96 -0
  148. package/.next/static/chunks/00047pvxj~z3x.js +0 -4
  149. package/.next/static/chunks/0ubkidns7e9dp.js +0 -1
  150. package/.next/static/chunks/0zpqg7iibb7fi.js +0 -1
  151. /package/.next/static/{1nsKFD4r1lqfWBZ-F7k3- → IKi4kxrMEeLozKLpLTtI7}/_buildManifest.js +0 -0
  152. /package/.next/static/{1nsKFD4r1lqfWBZ-F7k3- → IKi4kxrMEeLozKLpLTtI7}/_clientMiddlewareManifest.js +0 -0
  153. /package/.next/static/{1nsKFD4r1lqfWBZ-F7k3- → IKi4kxrMEeLozKLpLTtI7}/_ssgManifest.js +0 -0
@@ -0,0 +1,105 @@
1
+ /* Round 477 verification: legend pin-ring (R181 always-mount stroke
2
+ * ring) gains filter: drop-shadow glow on isPinned. Extends R476's
3
+ * drop-shadow idiom from the hub-digit focal scope to the legend-
4
+ * row pin-state scope.
5
+ *
6
+ * Contract:
7
+ * - at rest (no pin): every legend-pin-ring has data-legend-pin-
8
+ * ring-glow='false' AND computed filter === 'none' AND opacity=0
9
+ * - click a legend status row to pin: that ring flips to
10
+ * glow='true' + filter starts with 'drop-shadow' + opacity=1
11
+ * - other (non-pinned) rings stay at rest
12
+ * - source-file conditional wired
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', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
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
+
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-legend-pin-ring]', { timeout: 15000 });
49
+ await page.waitForTimeout(500);
50
+
51
+ const readAll = () => page.evaluate(() => {
52
+ const rings = [...document.querySelectorAll('[data-legend-pin-ring]')];
53
+ return rings.map(r => {
54
+ const cs = getComputedStyle(r);
55
+ return {
56
+ key: r.getAttribute('data-legend-pin-ring'),
57
+ glow: r.getAttribute('data-legend-pin-ring-glow'),
58
+ pinned: r.getAttribute('data-legend-pin-ring-pinned'),
59
+ filter: cs.filter,
60
+ };
61
+ });
62
+ });
63
+
64
+ const restAll = await readAll();
65
+ // Click a legend row hitbox (R271 hitbox rect at y=row.y0-11..+11)
66
+ // Use data-legend-row-tinted as the hitbox handle — clicking it
67
+ // toggles pinnedStatus to its key.
68
+ const firstRow = await page.$('[data-legend-row-tinted]');
69
+ let pinnedAll = null;
70
+ if (firstRow) {
71
+ await firstRow.click();
72
+ await page.waitForTimeout(400);
73
+ pinnedAll = await readAll();
74
+ }
75
+
76
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
77
+ const sourceGlowAttr = /data-legend-pin-ring-glow=\{isPinned/.test(src);
78
+ const sourceDropShadow = /drop-shadow\(0 0 3px \$\{row\.fill\}88\)/.test(src);
79
+ const sourceFilterTween = /opacity 150ms ease-out, filter 200ms ease-out/.test(src);
80
+
81
+ await browser.close();
82
+
83
+ const restCount = restAll.length;
84
+ const restAllFalse = restAll.every(r => r.glow === 'false' && r.filter === 'none');
85
+ const pinnedTarget = pinnedAll?.find(r => r.glow === 'true');
86
+ const pinnedHasDropShadow = pinnedTarget && /drop-shadow/.test(pinnedTarget.filter);
87
+ const pinnedOthersStill = pinnedAll
88
+ ? pinnedAll.filter(r => r !== pinnedTarget).every(r => r.glow === 'false')
89
+ : false;
90
+
91
+ const results = {
92
+ rest_count_ge_3: restCount >= 3,
93
+ rest_all_glow_false: restAllFalse,
94
+ pinned_target_glow: pinnedTarget?.glow === 'true',
95
+ pinned_target_has_drop_shadow: pinnedHasDropShadow,
96
+ pinned_others_stay_rest: pinnedOthersStill,
97
+ source_glow_attr: sourceGlowAttr,
98
+ source_drop_shadow: sourceDropShadow,
99
+ source_filter_tween: sourceFilterTween,
100
+ };
101
+ const ok = Object.values(results).every(Boolean);
102
+ console.log(`${ok ? '✅' : '❌'} legend pin-ring drop-shadow glow:`, JSON.stringify(results),
103
+ '\n rest:', JSON.stringify(restAll),
104
+ '\n pinned target:', JSON.stringify(pinnedTarget));
105
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,93 @@
1
+ /* Round 475 verification: legend-row TEXT (row.label) transition
2
+ * cadence sync 150ms → 200ms. R473 lifted the legend-row TINT
3
+ * RECT but the text alongside still ran 150ms; R475 closes the
4
+ * legend-row scope internal desync mirroring R474 at recent-row.
5
+ *
6
+ * Contract:
7
+ * - every <text data-legend-row-label-transition='200ms'> renders
8
+ * (status rows: working / idle / offline / created / pending)
9
+ * - inline style includes 'fill 200ms ease-out' AND 'letter-
10
+ * spacing 200ms ease-out'
11
+ * - computed transitionDuration is '0.2s, 0.2s'
12
+ * - source-file conditional wired
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', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
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
+
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-legend-row-label-transition]', { timeout: 15000 });
49
+ await page.waitForTimeout(500);
50
+
51
+ const probe = await page.evaluate(() => {
52
+ const texts = [...document.querySelectorAll('[data-legend-row-label-transition]')];
53
+ return {
54
+ count: texts.length,
55
+ nodes: texts.map(t => {
56
+ const cs = getComputedStyle(t);
57
+ return {
58
+ attr: t.getAttribute('data-legend-row-label-transition'),
59
+ style: t.getAttribute('style') || '',
60
+ duration: cs.transitionDuration,
61
+ };
62
+ }),
63
+ };
64
+ });
65
+
66
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
67
+ const sourceAttr = /data-legend-row-label-transition="200ms"/.test(src);
68
+ const sourceStyle = /'fill 200ms ease-out, letter-spacing 200ms ease-out'/.test(src);
69
+
70
+ await browser.close();
71
+
72
+ const countGe2 = probe.count >= 2;
73
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
74
+ const allStyle200 = probe.nodes.every(n =>
75
+ /fill 200ms ease-out/.test(n.style) && /letter-spacing 200ms ease-out/.test(n.style));
76
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
77
+ const noLegacy150 = probe.nodes.every(n =>
78
+ !/fill 150ms/.test(n.style) && !/letter-spacing 150ms/.test(n.style));
79
+
80
+ const results = {
81
+ text_count_ge_2: countGe2,
82
+ all_attr_200: allAttr200,
83
+ all_style_200: allStyle200,
84
+ all_computed_200: allDur200,
85
+ no_legacy_150_in_dom: noLegacy150,
86
+ source_attr: sourceAttr,
87
+ source_style: sourceStyle,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} legend-row text cadence 150→200:`, JSON.stringify(results),
91
+ '\n count:', probe.count,
92
+ '\n first:', JSON.stringify(probe.nodes[0]));
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,92 @@
1
+ /* Round 473 verification: legend-row tint rect transition cadence
2
+ * sync 150ms → 200ms. Final closure of the 3-tier panel-row cadence
3
+ * family — group-label (R459), recent-row (R472), legend-row (R473).
4
+ * All three label-class panel-row hitboxes now ease their fill +
5
+ * opacity at uniform 200ms ease-out.
6
+ *
7
+ * Contract:
8
+ * - every <rect data-legend-row-tint-transition='200ms'> renders
9
+ * (one per status legend row — 5 rows: working/idle/offline/+)
10
+ * - inline style is 'fill 200ms ease-out, opacity 200ms ease-out'
11
+ * - computed transition-duration is '0.2s, 0.2s'
12
+ * - NO 'fill 150ms' substring anywhere in any legend-row tint
13
+ * - source-file attr + style both wired
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('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
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
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-legend-row-tint-transition]', { timeout: 15000 });
50
+ await page.waitForTimeout(500);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const rects = [...document.querySelectorAll('[data-legend-row-tint-transition]')];
54
+ return {
55
+ count: rects.length,
56
+ nodes: rects.map(r => {
57
+ const cs = getComputedStyle(r);
58
+ return {
59
+ attr: r.getAttribute('data-legend-row-tint-transition'),
60
+ style: r.getAttribute('style') || '',
61
+ duration: cs.transitionDuration,
62
+ };
63
+ }),
64
+ };
65
+ });
66
+
67
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
68
+ const sourceAttr = /data-legend-row-tint-transition="200ms"/.test(src);
69
+ const source150Removed = !src.includes('fill 150ms ease-out, opacity 150ms ease-out');
70
+
71
+ await browser.close();
72
+
73
+ const countGe2 = probe.count >= 2;
74
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
75
+ const allStyle200 = probe.nodes.every(n => /fill 200ms ease-out, opacity 200ms ease-out/.test(n.style));
76
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
77
+ const noLegacy150 = probe.nodes.every(n => !/fill 150ms/.test(n.style));
78
+
79
+ const results = {
80
+ legend_rect_count_ge_2: countGe2,
81
+ all_attr_200: allAttr200,
82
+ all_style_200: allStyle200,
83
+ all_computed_200: allDur200,
84
+ no_legacy_150_in_dom: noLegacy150,
85
+ source_attr: sourceAttr,
86
+ source_no_150_anywhere: source150Removed,
87
+ };
88
+ const ok = Object.values(results).every(Boolean);
89
+ console.log(`${ok ? '✅' : '❌'} legend-row tint cadence 150→200:`, JSON.stringify(results),
90
+ '\n count:', probe.count,
91
+ '\n first:', JSON.stringify(probe.nodes[0]));
92
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,100 @@
1
+ /* Round 478 verification: recent-row freshness pip gains filter:
2
+ * drop-shadow glow when alpha > 0.7 (just-fired flow within ~30s).
3
+ * Freshness-gated (not pin/hover-gated), so the glow reads as
4
+ * "this signal is live" — natural breathing feel that tracks
5
+ * actual data freshness.
6
+ *
7
+ * Contract:
8
+ * - fresh (created_at = now): alpha=1.0, data-recent-row-freshness-
9
+ * glow='true', computed filter starts with 'drop-shadow'
10
+ * - stale (created_at = 5min ago): alpha→0.30, glow='false',
11
+ * filter === 'none'
12
+ * - source-file conditional wired
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 nowIso = () => new Date().toISOString();
19
+ const sessionFresh = 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: sessionFresh, updated_at: sessionFresh, last_seen_at: sessionFresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('b·1', 'working'),
42
+ ] } });
43
+ });
44
+ // Mix fresh + stale messages so we get rows in both freshness tiers.
45
+ // Fresh: created NOW → alpha = 1.0 (within 30s gate)
46
+ // Stale: created 5min ago → alpha = 0.30 (R10 stale floor)
47
+ const fiveMinAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
48
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: {
49
+ messages: [
50
+ { id: 'm1', from_alias: 'a·1', to_alias: 'a·2', content: 'live', created_at: nowIso() },
51
+ { id: 'm2', from_alias: 'b·1', to_alias: 'a·1', content: 'stale', created_at: fiveMinAgo },
52
+ ],
53
+ } }));
54
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
55
+
56
+ const page = await ctx.newPage();
57
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
58
+ await page.waitForSelector('[data-recent-row-freshness]', { timeout: 15000 });
59
+ await page.waitForTimeout(800);
60
+
61
+ const probe = await page.evaluate(() => {
62
+ const pips = [...document.querySelectorAll('[data-recent-row-freshness]')];
63
+ return pips.map(p => {
64
+ const cs = getComputedStyle(p);
65
+ return {
66
+ key: p.getAttribute('data-recent-row-freshness'),
67
+ alpha: parseFloat(p.getAttribute('data-recent-row-freshness-alpha') || '0'),
68
+ glow: p.getAttribute('data-recent-row-freshness-glow'),
69
+ filter: cs.filter,
70
+ };
71
+ });
72
+ });
73
+
74
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
75
+ const sourceGlowAttr = /data-recent-row-freshness-glow=\{alpha > 0\.7/.test(src);
76
+ const sourceDropShadow = /drop-shadow\(0 0 3px \$\{pal\.legendAccent\}80\)/.test(src);
77
+ const sourceFilterTween = /filter 200ms ease-out/.test(src);
78
+
79
+ await browser.close();
80
+
81
+ const fresh = probe.find(p => p.alpha > 0.7);
82
+ const stale = probe.find(p => p.alpha < 0.5);
83
+
84
+ const results = {
85
+ pips_count_ge_2: probe.length >= 2,
86
+ fresh_pip_found: !!fresh,
87
+ fresh_glow_true: fresh?.glow === 'true',
88
+ fresh_filter_has_drop: fresh && /drop-shadow/.test(fresh.filter),
89
+ stale_pip_found: !!stale,
90
+ stale_glow_false: stale?.glow === 'false',
91
+ stale_filter_none: stale?.filter === 'none',
92
+ source_glow_attr: sourceGlowAttr,
93
+ source_drop_shadow: sourceDropShadow,
94
+ source_filter_tween: sourceFilterTween,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} recent-row freshness pip drop-shadow:`, JSON.stringify(results),
98
+ '\n fresh pip:', JSON.stringify(fresh),
99
+ '\n stale pip:', JSON.stringify(stale));
100
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,99 @@
1
+ /* Round 474 verification: recent-row TEXT (alias / count / preview)
2
+ * transition cadence sync 150ms → 200ms. R472 lifted the row TINT
3
+ * RECT to 200ms but the text alongside still ran 150ms; R474 closes
4
+ * that internal recent-row scope desync.
5
+ *
6
+ * Contract:
7
+ * - every <text data-recent-row-text-transition='200ms'> renders
8
+ * for each active recent-signal row
9
+ * - inline style includes 'fill 200ms ease-out' AND 'letter-
10
+ * spacing 200ms ease-out'
11
+ * - computed transitionDuration is '0.2s, 0.2s'
12
+ * - source-file conditional wired
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', 'working'), mk('a·2', 'idle'), mk('b·1', 'working'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: {
44
+ messages: [
45
+ { id: 'm1', from_alias: 'a·1', to_alias: 'a·2', content: 'ping', created_at: fresh },
46
+ { id: 'm2', from_alias: 'b·1', to_alias: 'a·1', content: 'pong', created_at: fresh },
47
+ { id: 'm3', from_alias: 'a·2', to_alias: 'b·1', content: 'reply', created_at: fresh },
48
+ ],
49
+ } }));
50
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
51
+
52
+ const page = await ctx.newPage();
53
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
54
+ await page.waitForSelector('[data-recent-row-text-transition]', { timeout: 15000 });
55
+ await page.waitForTimeout(500);
56
+
57
+ const probe = await page.evaluate(() => {
58
+ const texts = [...document.querySelectorAll('[data-recent-row-text-transition]')];
59
+ return {
60
+ count: texts.length,
61
+ nodes: texts.map(t => {
62
+ const cs = getComputedStyle(t);
63
+ return {
64
+ attr: t.getAttribute('data-recent-row-text-transition'),
65
+ style: t.getAttribute('style') || '',
66
+ duration: cs.transitionDuration,
67
+ };
68
+ }),
69
+ };
70
+ });
71
+
72
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
73
+ const sourceAttr = /data-recent-row-text-transition="200ms"/.test(src);
74
+ const sourceStyle = /'fill 200ms ease-out, letter-spacing 200ms ease-out'/.test(src);
75
+
76
+ await browser.close();
77
+
78
+ const countGe1 = probe.count >= 1;
79
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
80
+ const allStyle200 = probe.nodes.every(n =>
81
+ /fill 200ms ease-out/.test(n.style) && /letter-spacing 200ms ease-out/.test(n.style));
82
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
83
+ const noLegacy150 = probe.nodes.every(n =>
84
+ !/fill 150ms/.test(n.style) && !/letter-spacing 150ms/.test(n.style));
85
+
86
+ const results = {
87
+ text_count_ge_1: countGe1,
88
+ all_attr_200: allAttr200,
89
+ all_style_200: allStyle200,
90
+ all_computed_200: allDur200,
91
+ no_legacy_150_in_dom: noLegacy150,
92
+ source_attr: sourceAttr,
93
+ source_style: sourceStyle,
94
+ };
95
+ const ok = Object.values(results).every(Boolean);
96
+ console.log(`${ok ? '✅' : '❌'} recent-row text cadence 150→200:`, JSON.stringify(results),
97
+ '\n count:', probe.count,
98
+ '\n first:', JSON.stringify(probe.nodes[0]));
99
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,96 @@
1
+ /* Round 472 verification: recent-signal row tint rect transition
2
+ * cadence sync 150ms → 200ms. Sibling idiom to R459 (group-label
3
+ * hitbox tint sync) at the recent-signal row tier. Closes the
4
+ * last 150ms transition still hiding in the panel tier.
5
+ *
6
+ * Contract:
7
+ * - every <rect data-recent-row-tint-transition="200ms"> renders
8
+ * for each active recent-signal row (gated on flowLinks > 0)
9
+ * - computed transition-duration is '0.2s'
10
+ * - source-file conditional wired
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
+ await route.fulfill({ response: r, json: { ...b, sessions: [
38
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('b·1', 'working'),
39
+ ] } });
40
+ });
41
+ // Inject flow-link messages so recent-signal panel renders
42
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: {
43
+ messages: [
44
+ { id: 'm1', from_alias: 'a·1', to_alias: 'a·2', content: 'ping', created_at: fresh },
45
+ { id: 'm2', from_alias: 'b·1', to_alias: 'a·1', content: 'pong', created_at: fresh },
46
+ { id: 'm3', from_alias: 'a·2', to_alias: 'b·1', content: 'reply', created_at: fresh },
47
+ ],
48
+ } }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
53
+ await page.waitForSelector('[data-recent-row-tint-transition]', { timeout: 15000 });
54
+ await page.waitForTimeout(800);
55
+
56
+ const probe = await page.evaluate(() => {
57
+ const rects = [...document.querySelectorAll('[data-recent-row-tint-transition]')];
58
+ return {
59
+ count: rects.length,
60
+ nodes: rects.map(r => {
61
+ const cs = getComputedStyle(r);
62
+ return {
63
+ attr: r.getAttribute('data-recent-row-tint-transition'),
64
+ style: r.getAttribute('style') || '',
65
+ duration: cs.transitionDuration,
66
+ };
67
+ }),
68
+ };
69
+ });
70
+
71
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
72
+ const sourceAttr = /data-recent-row-tint-transition="200ms"/.test(src);
73
+ const sourceStyle = /transition: 'fill 200ms ease-out, opacity 200ms ease-out' \}\}\n {20}\/>/.test(src) ||
74
+ /'fill 200ms ease-out, opacity 200ms ease-out'/.test(src);
75
+ await browser.close();
76
+
77
+ const countGe1 = probe.count >= 1;
78
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
79
+ const allStyle200 = probe.nodes.every(n => /fill 200ms ease-out, opacity 200ms ease-out/.test(n.style));
80
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
81
+ const noLegacy150 = probe.nodes.every(n => !/fill 150ms/.test(n.style));
82
+
83
+ const results = {
84
+ tint_count_ge_1: countGe1,
85
+ all_attr_200: allAttr200,
86
+ all_style_200: allStyle200,
87
+ all_computed_200: allDur200,
88
+ no_legacy_150: noLegacy150,
89
+ source_attr: sourceAttr,
90
+ source_style: sourceStyle,
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} recent-row tint cadence 150→200:`, JSON.stringify(results),
94
+ '\n count:', probe.count,
95
+ '\n first:', JSON.stringify(probe.nodes[0]));
96
+ process.exit(ok ? 0 : 1);