@sleep2agi/agent-network-dashboard 0.5.1-preview.9 → 0.5.1-preview.91
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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +6 -6
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +2 -2
- package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +2 -2
- package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +2 -2
- package/.next/server/app/node.segments/_full.segment.rsc +2 -2
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +2 -2
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +2 -2
- package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +2 -2
- package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +3 -3
- package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +2 -2
- package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/017hq2-5l~_98.css +2 -0
- package/.next/static/chunks/07t9_p5h7da1u.js +1 -0
- package/.next/static/chunks/0ahff_xzbgbg4.js +4 -0
- package/.next/static/chunks/10qa7z9iocn6t.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +1777 -111
- package/package.json +1 -1
- package/scripts/topo-active-links-chip-hover-lift-test.mjs +93 -0
- package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
- package/scripts/topo-chip-digit-hover-bold-test.mjs +94 -0
- package/scripts/topo-chip-row-group-hover-brighten-test.mjs +107 -0
- package/scripts/topo-chip-row-hover-lift-test.mjs +95 -0
- package/scripts/topo-chrome-button-hover-lift-test.mjs +94 -0
- package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
- package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
- package/scripts/topo-edge-badge-digit-fw-test.mjs +103 -0
- package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
- package/scripts/topo-edge-badge-hover-opacity-test.mjs +94 -0
- package/scripts/topo-edge-badge-hover-stroke-test.mjs +92 -0
- package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
- package/scripts/topo-edge-badge-pin-opacity-test.mjs +86 -0
- package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
- package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
- package/scripts/topo-edge-particle-radius-test.mjs +76 -0
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
- package/scripts/topo-filter-pill-hover-lift-test.mjs +101 -0
- package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
- package/scripts/topo-filter-pill-value-fw-test.mjs +88 -0
- package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
- package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
- package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
- package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
- package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
- package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
- package/scripts/topo-fullscreen-icon-hover-scale-test.mjs +91 -0
- package/scripts/topo-group-box-stroke-test.mjs +105 -0
- package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
- package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
- package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
- package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
- package/scripts/topo-hover-detail-rx-test.mjs +81 -0
- package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
- package/scripts/topo-hub-digit-fw-hover-test.mjs +102 -0
- package/scripts/topo-hub-halo-light-trough-test.mjs +88 -0
- package/scripts/topo-hub-halo-radius-test.mjs +86 -0
- package/scripts/topo-hub-halo-trough-test.mjs +83 -0
- package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
- package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
- package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
- package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
- package/scripts/topo-hub-spoke-hover-opacity-test.mjs +119 -0
- package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
- package/scripts/topo-label-card-opacity-hover-test.mjs +99 -0
- package/scripts/topo-layout-toggle-hover-tracking-test.mjs +109 -0
- package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
- package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
- package/scripts/topo-legend-pin-ring-stroke-test.mjs +101 -0
- package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
- package/scripts/topo-minimap-online-hover-opacity-test.mjs +92 -0
- package/scripts/topo-minimap-online-opacity-test.mjs +93 -0
- package/scripts/topo-minimap-online-radius-test.mjs +85 -0
- package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
- package/scripts/topo-minimap-viewport-rx-test.mjs +85 -0
- package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
- package/scripts/topo-node-alias-letter-spacing-test.mjs +112 -0
- package/scripts/topo-node-halo-offline-opacity-test.mjs +87 -0
- package/scripts/topo-node-label-card-rx-test.mjs +85 -0
- package/scripts/topo-node-pulse-peak-test.mjs +89 -0
- package/scripts/topo-node-pulse-trough-test.mjs +91 -0
- package/scripts/topo-node-sub-text-letter-spacing-test.mjs +115 -0
- package/scripts/topo-panel-count-fw-hover-test.mjs +105 -0
- package/scripts/topo-panel-count-letterspacing-test.mjs +89 -0
- package/scripts/topo-panel-stroke-hover-test.mjs +110 -0
- package/scripts/topo-pressure-bar-height-test.mjs +92 -0
- package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
- package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
- package/scripts/topo-recent-pip-radius-test.mjs +76 -0
- package/scripts/topo-recent-row-content-opacity-test.mjs +81 -0
- package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
- package/scripts/topo-reset-hover-rotate-test.mjs +102 -0
- package/scripts/topo-spoke-active-opacity-test.mjs +104 -0
- package/scripts/topo-spoke-active-stroke-test.mjs +95 -0
- package/scripts/topo-spoke-idle-opacity-test.mjs +91 -0
- package/scripts/topo-vendor-chip-hover-lift-test.mjs +87 -0
- package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
- package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
- package/scripts/topo-vendor-suffix-hover-brighten-test.mjs +87 -0
- package/scripts/topo-zoom-icon-hover-scale-test.mjs +114 -0
- package/scripts/topo-zoom-level-hover-fw-test.mjs +95 -0
- package/.next/static/chunks/08fc_cz1nk7b9.js +0 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/0e0okm.affulg.js +0 -1
- package/.next/static/chunks/0s3vtwfo26_t6.js +0 -4
- /package/.next/static/{egukPz1ctU--4WnT2FpEU → i0drwZtW8h-M1ML2C5VZF}/_buildManifest.js +0 -0
- /package/.next/static/{egukPz1ctU--4WnT2FpEU → i0drwZtW8h-M1ML2C5VZF}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{egukPz1ctU--4WnT2FpEU → i0drwZtW8h-M1ML2C5VZF}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 365 verification: hub-center 'lit-lamp' decorative highlight
|
|
2
|
+
* circle radius 5 → 5.5. Visible when workingCount === 0 (idle fleet
|
|
3
|
+
* decor per R130). Sibling visual-weight bump family — extends to
|
|
4
|
+
* 6 canvas anchors:
|
|
5
|
+
* R287 minimap viewport stroke 1 → 1.5
|
|
6
|
+
* R295 legend swatch base radius 5.5 → 6
|
|
7
|
+
* R359 recent-row pip base radius 1.6 → 1.8
|
|
8
|
+
* R360 hub digit fontSize 11 → 12
|
|
9
|
+
* R361 edge-badge digit fontSize 10 → 11
|
|
10
|
+
* R365 hub-highlight base radius 5 → 5.5 (this round)
|
|
11
|
+
*
|
|
12
|
+
* 21 % area bump (π·5.5² / π·5² = 1.21). Still well inside r=10 core.
|
|
13
|
+
*
|
|
14
|
+
* Test approach: feed an IDLE fleet (no working sessions) so the
|
|
15
|
+
* highlight is fully opaque.
|
|
16
|
+
*
|
|
17
|
+
* Contract:
|
|
18
|
+
* - data-topo-hub-highlight element r attr === '5.5'.
|
|
19
|
+
* - data-topo-hub-highlight-radius === '5.5'.
|
|
20
|
+
* - data-topo-hub-highlight-visible === 'true' (idle fleet).
|
|
21
|
+
* - opacity attr === '0.9' (workingCount === 0 branch).
|
|
22
|
+
* - Pre-R365 invariants:
|
|
23
|
+
* * cx + cy unchanged (visual center stable)
|
|
24
|
+
* * fill='#d1fae5' (R130 lamp color)
|
|
25
|
+
* * style.transition contains opacity (R213 always-mount gate)
|
|
26
|
+
* * pointerEvents: 'none' (R130)
|
|
27
|
+
*/
|
|
28
|
+
import { chromium } from 'playwright';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
30
|
+
|
|
31
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
32
|
+
const browser = await chromium.launch({ headless: true });
|
|
33
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
34
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
35
|
+
await ctx.addInitScript(() => {
|
|
36
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
37
|
+
});
|
|
38
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
39
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
40
|
+
const r = await route.fetch();
|
|
41
|
+
const b = await r.json();
|
|
42
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
43
|
+
// IDLE: status !== 'working' so workingCount=0 and the highlight is opaque.
|
|
44
|
+
const mk = (alias) => ({
|
|
45
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
const probe = await page.evaluate(() => {
|
|
60
|
+
const h = document.querySelector('[data-topo-hub-highlight]');
|
|
61
|
+
const cs = h ? getComputedStyle(h) : null;
|
|
62
|
+
return {
|
|
63
|
+
rAttr: h?.getAttribute('r') ?? null,
|
|
64
|
+
rData: h?.getAttribute('data-topo-hub-highlight-radius') ?? null,
|
|
65
|
+
visibleAttr: h?.getAttribute('data-topo-hub-highlight-visible') ?? null,
|
|
66
|
+
opacityAttr: h?.getAttribute('opacity') ?? null,
|
|
67
|
+
fill: h?.getAttribute('fill') ?? null,
|
|
68
|
+
transition: cs?.transition ?? null,
|
|
69
|
+
pointerEv: cs?.pointerEvents ?? null,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const hasTrans = (s, prop) =>
|
|
76
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
r_attr_5_5: probe.rAttr === '5.5',
|
|
80
|
+
r_data_5_5: probe.rData === '5.5',
|
|
81
|
+
visible_true: probe.visibleAttr === 'true',
|
|
82
|
+
opacity_0_9: probe.opacityAttr === '0.9',
|
|
83
|
+
fill_d1fae5: probe.fill === '#d1fae5',
|
|
84
|
+
trans_has_opacity: hasTrans(probe.transition, 'opacity'),
|
|
85
|
+
pointer_events_none: probe.pointerEv === 'none',
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} hub-highlight radius 5 → 5.5:`, JSON.stringify(results),
|
|
89
|
+
'\n probe:', probe);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* Round 370 verification: hub hover-ring cyber opacity 0.7 → 0.8.
|
|
2
|
+
* R177 designed the hub hover-ring at opacity-0 → 0.85 (light) / 0 →
|
|
3
|
+
* 0.7 (cyber). The 15 % gap between themes meant cyber-theme users
|
|
4
|
+
* got a noticeably softer hover cue than light-theme users against
|
|
5
|
+
* backgrounds that should equalise (dark bg needs more luminance to
|
|
6
|
+
* read as 'on'). R370 bumps cyber 0.7 → 0.8, narrowing the theme
|
|
7
|
+
* gap to 5 % (light stays at 0.85).
|
|
8
|
+
*
|
|
9
|
+
* Contract (cyber theme):
|
|
10
|
+
* - Rest (hub NOT hovered): hub-hover-ring opacity attr === '0'.
|
|
11
|
+
* - Hover hub: opacity attr === '0.8'.
|
|
12
|
+
* - data-topo-hub-hover-ring-opacity === '0.8' on hover.
|
|
13
|
+
* - Pre-R370 invariants:
|
|
14
|
+
* * r=17 on hover (R177)
|
|
15
|
+
* * stroke=#10b981 (cyber, R253)
|
|
16
|
+
* * strokeWidth=1.5
|
|
17
|
+
* * pointerEvents:none
|
|
18
|
+
* * R177 r + opacity transitions preserved
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
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
|
+
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('[data-topo-hub-hover-ring]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(300);
|
|
49
|
+
|
|
50
|
+
const restProbe = await page.evaluate(() => {
|
|
51
|
+
const r = document.querySelector('[data-topo-hub-hover-ring]');
|
|
52
|
+
const cs = r ? getComputedStyle(r) : null;
|
|
53
|
+
return {
|
|
54
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
55
|
+
opacityData: r?.getAttribute('data-topo-hub-hover-ring-opacity') ?? null,
|
|
56
|
+
rAttr: r?.getAttribute('r') ?? null,
|
|
57
|
+
radiusData: r?.getAttribute('data-topo-hub-hover-ring-radius') ?? null,
|
|
58
|
+
strokeAttr: r?.getAttribute('stroke') ?? null,
|
|
59
|
+
strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
|
|
60
|
+
pointerEv: cs?.pointerEvents ?? null,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Hover the hub group.
|
|
65
|
+
await page.hover('[data-topo-hub]');
|
|
66
|
+
await page.waitForTimeout(300);
|
|
67
|
+
const hoverProbe = await page.evaluate(() => {
|
|
68
|
+
const r = document.querySelector('[data-topo-hub-hover-ring]');
|
|
69
|
+
return {
|
|
70
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
71
|
+
opacityData: r?.getAttribute('data-topo-hub-hover-ring-opacity') ?? null,
|
|
72
|
+
rAttr: r?.getAttribute('r') ?? null,
|
|
73
|
+
radiusData: r?.getAttribute('data-topo-hub-hover-ring-radius') ?? null,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
rest_opacity_0: restProbe.opacityAttr === '0',
|
|
81
|
+
rest_opacity_data_0: restProbe.opacityData === '0',
|
|
82
|
+
rest_r_14: restProbe.rAttr === '14', // R177 rest
|
|
83
|
+
rest_radius_data_14: restProbe.radiusData === '14',
|
|
84
|
+
rest_stroke_cyber: restProbe.strokeAttr === '#10b981', // R253 cyber
|
|
85
|
+
rest_stroke_width_1_5: restProbe.strokeWidthAttr === '1.5', // R51 sentinel reserved but R177
|
|
86
|
+
pointer_events_none: restProbe.pointerEv === 'none',
|
|
87
|
+
hover_opacity_0_8: hoverProbe.opacityAttr === '0.8', // R370 cyber bump
|
|
88
|
+
hover_opacity_data_0_8: hoverProbe.opacityData === '0.8',
|
|
89
|
+
hover_r_17: hoverProbe.rAttr === '17', // R177 hover lift
|
|
90
|
+
hover_radius_data_17: hoverProbe.radiusData === '17',
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} hub hover-ring opacity 0.7 → 0.8 (cyber):`, JSON.stringify(results),
|
|
94
|
+
'\n rest: ', restProbe,
|
|
95
|
+
'\n hover:', hoverProbe);
|
|
96
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Round 385 verification: hub hover-ring strokeWidth 1.5 → 1.75.
|
|
2
|
+
* Sibling visual-weight bump (11th anchor) to R367 edge-badge rest
|
|
3
|
+
* stroke 1 → 1.25. The ring is only visible during hub hover
|
|
4
|
+
* (opacity=0 rest), so this manifests as a thicker hover-state
|
|
5
|
+
* ring on the canvas focal point.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - [data-topo-hub-hover-ring] stroke-width attr === '1.75'.
|
|
9
|
+
* - data-topo-hub-hover-ring-stroke-width === '1.75'.
|
|
10
|
+
* - Pre-R385 invariants on the same circle:
|
|
11
|
+
* * R177 r=14 (rest) / 17 (hover)
|
|
12
|
+
* * R253 stroke = isLight ? '#059669' : '#10b981'
|
|
13
|
+
* * R370 opacity 0 (rest) / 0.8 cyber on hover
|
|
14
|
+
* * pointerEvents:none
|
|
15
|
+
* - Stays clear of R51 sentinel strokeWidth 3 (1.75 < 3).
|
|
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 browser = await chromium.launch({ headless: true });
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
26
|
+
});
|
|
27
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
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: 'working', 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'), mk('b'), mk('c') ] } });
|
|
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
|
+
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForSelector('[data-topo-hub-hover-ring]', { timeout: 15000 });
|
|
45
|
+
await page.waitForTimeout(300);
|
|
46
|
+
|
|
47
|
+
const restProbe = await page.evaluate(() => {
|
|
48
|
+
const r = document.querySelector('[data-topo-hub-hover-ring]');
|
|
49
|
+
return {
|
|
50
|
+
strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
|
|
51
|
+
strokeWidthData: r?.getAttribute('data-topo-hub-hover-ring-stroke-width') ?? null,
|
|
52
|
+
rAttr: r?.getAttribute('r') ?? null,
|
|
53
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
54
|
+
strokeAttr: r?.getAttribute('stroke') ?? null,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Hover hub to verify visible state.
|
|
59
|
+
await page.hover('[data-topo-hub]');
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
const hoverProbe = await page.evaluate(() => {
|
|
62
|
+
const r = document.querySelector('[data-topo-hub-hover-ring]');
|
|
63
|
+
return {
|
|
64
|
+
strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
|
|
65
|
+
rAttr: r?.getAttribute('r') ?? null,
|
|
66
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await browser.close();
|
|
71
|
+
|
|
72
|
+
const results = {
|
|
73
|
+
rest_stroke_width_1_75: restProbe.strokeWidthAttr === '1.75',
|
|
74
|
+
rest_data_stroke_1_75: restProbe.strokeWidthData === '1.75',
|
|
75
|
+
rest_r_14: restProbe.rAttr === '14', // R177 rest
|
|
76
|
+
rest_opacity_0: restProbe.opacityAttr === '0',
|
|
77
|
+
rest_stroke_cyber: restProbe.strokeAttr === '#10b981', // R253 cyber
|
|
78
|
+
hover_stroke_width_1_75: hoverProbe.strokeWidthAttr === '1.75', // strokeWidth doesn't change with hover
|
|
79
|
+
hover_r_17: hoverProbe.rAttr === '17', // R177 hover lift
|
|
80
|
+
hover_opacity_0_8: hoverProbe.opacityAttr === '0.8', // R370 cyber bump
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} hub hover-ring strokeWidth 1.5 → 1.75:`, JSON.stringify(results),
|
|
84
|
+
'\n rest: ', restProbe,
|
|
85
|
+
'\n hover:', hoverProbe);
|
|
86
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Round 430 verification: hub-spoke opacity hover lift on
|
|
2
|
+
* hoveredAlias === session.alias. Idle 0.50 → 0.70; active 0.80 → 0.95.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* - rest: all idle spokes report data-topo-hub-spoke-opacity '0.5'
|
|
6
|
+
* and data-topo-hub-spoke-hovered 'false'
|
|
7
|
+
* - hover one node: that node's spoke opacity '0.7' (idle case)
|
|
8
|
+
* + data-topo-hub-spoke-hovered 'true'
|
|
9
|
+
* - siblings stay at rest 0.5
|
|
10
|
+
* - active state branch tested via source-file (deterministic
|
|
11
|
+
* active fixture is harder — flow link timing). The runtime DOM
|
|
12
|
+
* check covers the idle path; source-file probe covers both
|
|
13
|
+
* branches symmetrically.
|
|
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: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
37
|
+
mk('alpha', 'working'),
|
|
38
|
+
mk('beta', 'idle'),
|
|
39
|
+
mk('gamma', 'idle'),
|
|
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
|
+
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('[data-topo-hub-spoke-active]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const readAll = () => page.evaluate(() => {
|
|
51
|
+
const paths = [...document.querySelectorAll('[data-topo-hub-spoke-active]')];
|
|
52
|
+
// The spokes are keyed by `hub-${alias}`. We can't easily key them
|
|
53
|
+
// back by alias from data attrs, so we just snapshot their attrs.
|
|
54
|
+
return paths.map((p, i) => ({
|
|
55
|
+
idx: i,
|
|
56
|
+
active: p.getAttribute('data-topo-hub-spoke-active'),
|
|
57
|
+
hovered: p.getAttribute('data-topo-hub-spoke-hovered'),
|
|
58
|
+
opacity: p.getAttribute('data-topo-hub-spoke-opacity'),
|
|
59
|
+
opacity_a: p.getAttribute('opacity'),
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const rest = await readAll();
|
|
64
|
+
|
|
65
|
+
// Hover the first node group
|
|
66
|
+
const firstAlias = await page.evaluate(() => {
|
|
67
|
+
const t = document.querySelector('[data-node-alias-text]');
|
|
68
|
+
return t?.getAttribute('data-node-alias-text');
|
|
69
|
+
});
|
|
70
|
+
let hover = null;
|
|
71
|
+
if (firstAlias) {
|
|
72
|
+
const box = await page.evaluate((alias) => {
|
|
73
|
+
const t = document.querySelector(`[data-node-alias-text="${alias}"]`);
|
|
74
|
+
if (!t) return null;
|
|
75
|
+
const node = t.closest('[data-node]');
|
|
76
|
+
const target = node || t;
|
|
77
|
+
const b = target.getBoundingClientRect();
|
|
78
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
79
|
+
}, firstAlias);
|
|
80
|
+
if (box) {
|
|
81
|
+
await page.mouse.move(box.x, box.y);
|
|
82
|
+
await page.waitForTimeout(300);
|
|
83
|
+
hover = await readAll();
|
|
84
|
+
await page.mouse.move(0, 0);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Source-file probe
|
|
89
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
90
|
+
const sourceIsActive = /isActiveSpoke\s*\?\s*\(isHoveredSpoke\s*\?\s*0\.95\s*:\s*0\.80\)\s*:\s*\(isHoveredSpoke\s*\?\s*0\.70\s*:\s*0\.50\)/.test(fileText);
|
|
91
|
+
const sourceIsHovered = /const isHoveredSpoke = !reducedMotion && hoveredAlias === session\.alias/.test(fileText);
|
|
92
|
+
|
|
93
|
+
await browser.close();
|
|
94
|
+
|
|
95
|
+
const restAllIdle = rest.length > 0 && rest.every(r => r.active === 'false');
|
|
96
|
+
const restAllOpacity = rest.every(r => r.opacity === '0.5');
|
|
97
|
+
const restNoneHover = rest.every(r => r.hovered === 'false');
|
|
98
|
+
const hoveredCount = hover ? hover.filter(s => s.hovered === 'true').length : -1;
|
|
99
|
+
// idle-state hover lift: opacity 0.7
|
|
100
|
+
const hoveredEntry = hover?.find(s => s.hovered === 'true');
|
|
101
|
+
const hoveredOpacity = hoveredEntry?.opacity === '0.7';
|
|
102
|
+
const otherHoverNone = hover ? hover.filter(s => s.hovered === 'false').every(s => s.opacity === '0.5') : false;
|
|
103
|
+
|
|
104
|
+
const results = {
|
|
105
|
+
rest_spoke_count_ge_3: rest.length >= 3,
|
|
106
|
+
rest_all_idle: restAllIdle,
|
|
107
|
+
rest_all_opacity_0_5: restAllOpacity,
|
|
108
|
+
rest_no_hover: restNoneHover,
|
|
109
|
+
hover_exactly_one_match: hoveredCount === 1,
|
|
110
|
+
hover_target_opacity_0_7: hoveredOpacity,
|
|
111
|
+
hover_others_stay_rest: otherHoverNone,
|
|
112
|
+
source_three_tier_wired: sourceIsActive,
|
|
113
|
+
source_isHoveredSpoke_def: sourceIsHovered,
|
|
114
|
+
};
|
|
115
|
+
const ok = Object.values(results).every(Boolean);
|
|
116
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke hover opacity:`, JSON.stringify(results),
|
|
117
|
+
'\n rest sample:', JSON.stringify(rest[0]),
|
|
118
|
+
'\n hover target:', JSON.stringify(hoveredEntry));
|
|
119
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* Round 382 verification: hub-spoke path strokeLinecap='round'.
|
|
2
|
+
* Sibling SVG stroke-softening polish to R378 flow-rail dashes +
|
|
3
|
+
* R380 group box dashes — three dashed-stroke surfaces now share
|
|
4
|
+
* 'round' linecap.
|
|
5
|
+
*
|
|
6
|
+
* Dashed-stroke linecap family snapshot post-R382:
|
|
7
|
+
* R378 flow-rail '2 12' -> soft 3-px pills
|
|
8
|
+
* R380 group box '6 6' -> soft 7.5-px pills
|
|
9
|
+
* R382 hub spoke '6 14' -> soft 7-px pills (this round)
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Every [data-topo-hub-spoke-active] path has stroke-linecap='round'.
|
|
13
|
+
* - data-topo-hub-spoke-linecap === 'round'.
|
|
14
|
+
* - Idle spokes still have strokeDasharray='6 14' (R382 doesn't touch
|
|
15
|
+
* the dasharray); active spokes still have 'none'.
|
|
16
|
+
* - Pre-R382 invariants preserved on each spoke: strokeWidth (1 idle /
|
|
17
|
+
* 2 active), R241 stroke + stroke-width + opacity 250ms transition.
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias) => ({
|
|
35
|
+
alias, status: 'working', 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: [ mk('a'), mk('b'), mk('c') ] } });
|
|
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
|
+
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForSelector('[data-topo-hub-spoke-active]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
const probe = await page.evaluate(() => {
|
|
50
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
51
|
+
return spokes.map(el => ({
|
|
52
|
+
activeAttr: el.getAttribute('data-topo-hub-spoke-active'),
|
|
53
|
+
linecapAttr: el.getAttribute('stroke-linecap'),
|
|
54
|
+
linecapData: el.getAttribute('data-topo-hub-spoke-linecap'),
|
|
55
|
+
strokeWidthAttr: el.getAttribute('stroke-width'),
|
|
56
|
+
dasharrayAttr: el.getAttribute('stroke-dasharray'),
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await browser.close();
|
|
61
|
+
|
|
62
|
+
const allOk = probe.length >= 1 && probe.every(s =>
|
|
63
|
+
s.linecapAttr === 'round'
|
|
64
|
+
&& s.linecapData === 'round'
|
|
65
|
+
&& (s.activeAttr === 'true'
|
|
66
|
+
? (s.strokeWidthAttr === '2' && s.dasharrayAttr === 'none')
|
|
67
|
+
: (s.strokeWidthAttr === '1' && s.dasharrayAttr === '6 14'))
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
spokes_rendered: probe.length >= 1,
|
|
72
|
+
all_linecap_round: probe.every(s => s.linecapAttr === 'round'),
|
|
73
|
+
all_data_linecap: probe.every(s => s.linecapData === 'round'),
|
|
74
|
+
per_state_invariants_intact: allOk,
|
|
75
|
+
};
|
|
76
|
+
const ok = Object.values(results).every(Boolean);
|
|
77
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke strokeLinecap='round':`, JSON.stringify(results),
|
|
78
|
+
'\n count:', probe.length,
|
|
79
|
+
'\n sample:', probe.slice(0, 3));
|
|
80
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 429 verification: node label-card body opacity 0.94 → 1.0 on
|
|
2
|
+
* hover (cyber theme). Sibling to R348 panel rect opacity lift at
|
|
3
|
+
* panel scope. Adds a solidity axis to node hover signature alongside
|
|
4
|
+
* R217 stroke tint + R142 drop-shadow + R26 lift + R427/R428 ls.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - rest (cyber): every label-card reports opacity '0.94'
|
|
8
|
+
* - rest elevation attr 'idle' on all cards
|
|
9
|
+
* - hover one node: that card opacity '1' + elevation 'hover'
|
|
10
|
+
* - siblings stay at 0.94 / 'idle'
|
|
11
|
+
* - source-file probe confirms conditional + R246 transition intact
|
|
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: 1500 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
24
|
+
});
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias, status) => ({
|
|
30
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
35
|
+
mk('alpha', 'working'),
|
|
36
|
+
mk('beta', 'idle'),
|
|
37
|
+
mk('gamma', 'working'),
|
|
38
|
+
] } });
|
|
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
|
+
|
|
43
|
+
const page = await ctx.newPage();
|
|
44
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
45
|
+
await page.waitForSelector('[data-node-label-card]', { timeout: 15000 });
|
|
46
|
+
await page.waitForTimeout(400);
|
|
47
|
+
|
|
48
|
+
const readAll = () => page.evaluate(() => {
|
|
49
|
+
const rects = [...document.querySelectorAll('[data-node-label-card]')];
|
|
50
|
+
return rects.map(t => ({
|
|
51
|
+
alias: t.getAttribute('data-node-label-card'),
|
|
52
|
+
opacity: t.getAttribute('opacity'),
|
|
53
|
+
elevation: t.getAttribute('data-node-label-card-elevation'),
|
|
54
|
+
}));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const rest = await readAll();
|
|
58
|
+
const firstAlias = rest[0]?.alias;
|
|
59
|
+
let hover = null;
|
|
60
|
+
if (firstAlias) {
|
|
61
|
+
const box = await page.evaluate((alias) => {
|
|
62
|
+
const t = document.querySelector(`[data-node-label-card="${alias}"]`);
|
|
63
|
+
if (!t) return null;
|
|
64
|
+
const node = t.closest('[data-node]');
|
|
65
|
+
const target = node || t;
|
|
66
|
+
const b = target.getBoundingClientRect();
|
|
67
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
68
|
+
}, firstAlias);
|
|
69
|
+
if (box) {
|
|
70
|
+
await page.mouse.move(box.x, box.y);
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
hover = await readAll();
|
|
73
|
+
await page.mouse.move(0, 0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
78
|
+
const sourceWired = /opacity=\{\s*\n?\s*!reducedMotion && hoveredAlias === session\.alias\s*\n?\s*\?\s*1\s*\n?\s*:\s*\(isLight \? 1 : 0\.94\)\s*\n?\s*\}/.test(fileText);
|
|
79
|
+
const sourceTransition = /transition: 'filter 220ms ease-out, stroke 220ms ease-out, fill 220ms ease-out, opacity 220ms ease-out'/.test(fileText);
|
|
80
|
+
|
|
81
|
+
await browser.close();
|
|
82
|
+
|
|
83
|
+
const hoveredEntry = hover?.find(r => r.alias === firstAlias);
|
|
84
|
+
const othersRest = hover ? hover.filter(r => r.alias !== firstAlias).every(r => r.opacity === '0.94' && r.elevation === 'idle') : false;
|
|
85
|
+
|
|
86
|
+
const results = {
|
|
87
|
+
rest_card_count_gte_3: rest.length >= 3,
|
|
88
|
+
rest_all_opacity_0_94: rest.every(r => r.opacity === '0.94'),
|
|
89
|
+
rest_all_elevation_idle: rest.every(r => r.elevation === 'idle'),
|
|
90
|
+
hover_target_opacity_1: hoveredEntry?.opacity === '1',
|
|
91
|
+
hover_target_elevation: hoveredEntry?.elevation === 'hover',
|
|
92
|
+
hover_others_stay_idle: othersRest,
|
|
93
|
+
source_conditional_wired: sourceWired,
|
|
94
|
+
source_transition_intact: sourceTransition,
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} label card opacity hover:`, JSON.stringify(results),
|
|
98
|
+
'\n hover target:', JSON.stringify(hoveredEntry));
|
|
99
|
+
process.exit(ok ? 0 : 1);
|