@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,93 @@
|
|
|
1
|
+
/* Round 392 verification: minimap online dot opacity 0.9 → 0.95.
|
|
2
|
+
* Theme-consistency / canvas-presence polish family (7th anchor)
|
|
3
|
+
* after R370 hub-ring, R371 edge-badge, R372 minimap offline,
|
|
4
|
+
* R386 hub-highlight idle, R387 hover-detail panel, R391 hub-spoke
|
|
5
|
+
* active. Halves the online dot's idle alpha gap (0.10 → 0.05).
|
|
6
|
+
* Mirrors R386's hub-highlight idle 0.9 → 0.95 on the minimap.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - Minimap only mounts when view is non-default — test clicks
|
|
10
|
+
* zoom-in twice to mount it before probing.
|
|
11
|
+
* - Online dots: opacity attr === '0.95', data-opacity === '0.95',
|
|
12
|
+
* R384 r=1.9 invariant, data-online='true'
|
|
13
|
+
* - Offline dot (one ghost session): opacity attr === '0.6'
|
|
14
|
+
* (R372 invariant), r=1.2 invariant, data-online='false'
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
21
|
+
const stale = new Date(Date.now() - 30 * 60 * 1000).toISOString(); // 30 min ago → offline
|
|
22
|
+
|
|
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
|
+
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
|
+
await route.fulfill({ response: r, json: {
|
|
34
|
+
...b,
|
|
35
|
+
sessions: [
|
|
36
|
+
{ alias: 'alpha', status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
37
|
+
{ alias: 'beta', status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
38
|
+
{ alias: 'ghost', status: 'offline', model: 'claude-opus-4', runtime: 'claude-code-cli', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
39
|
+
],
|
|
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('g[data-node]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(300);
|
|
49
|
+
|
|
50
|
+
// Minimap mounts only on non-default view — click zoom-in twice
|
|
51
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
52
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
53
|
+
await page.waitForSelector('[data-topo-minimap-dot]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(400);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const dots = Array.from(document.querySelectorAll('[data-topo-minimap-dot]'));
|
|
58
|
+
const online = dots.filter((d) => d.getAttribute('data-topo-minimap-dot-online') === 'true');
|
|
59
|
+
const offline = dots.filter((d) => d.getAttribute('data-topo-minimap-dot-online') === 'false');
|
|
60
|
+
const read = (el) => el ? ({
|
|
61
|
+
opacityAttr: el.getAttribute('opacity'),
|
|
62
|
+
opacityData: el.getAttribute('data-topo-minimap-dot-opacity'),
|
|
63
|
+
radiusAttr: el.getAttribute('r'),
|
|
64
|
+
}) : null;
|
|
65
|
+
return {
|
|
66
|
+
total: dots.length,
|
|
67
|
+
onlineCount: online.length,
|
|
68
|
+
offlineCount: offline.length,
|
|
69
|
+
onlineSample: read(online[0]),
|
|
70
|
+
offlineSample: read(offline[0]),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
// Setup: 2 online + 1 offline
|
|
78
|
+
total_3_dots: probe.total === 3,
|
|
79
|
+
online_2_dots: probe.onlineCount === 2,
|
|
80
|
+
offline_1_dot: probe.offlineCount === 1,
|
|
81
|
+
// R392: online opacity 0.9 → 0.95
|
|
82
|
+
online_opacity_0_95: probe.onlineSample?.opacityAttr === '0.95',
|
|
83
|
+
online_data_0_95: probe.onlineSample?.opacityData === '0.95',
|
|
84
|
+
online_r_1_9: probe.onlineSample?.radiusAttr === '1.9', // R384 invariant
|
|
85
|
+
// R372 invariant: offline opacity 0.6
|
|
86
|
+
offline_opacity_0_6: probe.offlineSample?.opacityAttr === '0.6',
|
|
87
|
+
offline_data_0_6: probe.offlineSample?.opacityData === '0.6',
|
|
88
|
+
offline_r_1_2: probe.offlineSample?.radiusAttr === '1.2',
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} minimap online dot opacity 0.9 → 0.95:`, JSON.stringify(results),
|
|
92
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 384 verification: minimap online dot radius 1.7 → 1.9.
|
|
2
|
+
* Sibling visual-weight bump (10th anchor) to R383 recent-row pip
|
|
3
|
+
* 1.8 → 2.0. Widens online vs offline tier contrast (1.42× → 1.58×).
|
|
4
|
+
* R372 already lifted offline opacity 0.5 → 0.6; R384 lifts online
|
|
5
|
+
* radius — pair completes minimap-dot legibility polish across both
|
|
6
|
+
* states.
|
|
7
|
+
*
|
|
8
|
+
* The minimap only renders when view is non-default (zoom !== 1 ||
|
|
9
|
+
* pan), so test triggers zoom-in twice to mount it.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Online dot computed r === '1.9'.
|
|
13
|
+
* - Online dot data-topo-minimap-dot-radius === '1.9'.
|
|
14
|
+
* - Offline dot r === '1.2' (R198 invariant, untouched).
|
|
15
|
+
* - Offline dot data-radius === '1.2'.
|
|
16
|
+
* - Pre-R384 invariants:
|
|
17
|
+
* * R198 opacity (online 0.9 / offline 0.6 from R372)
|
|
18
|
+
* * R198 transition list (opacity + fill + r 200ms) 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
|
+
const stale = new Date(Date.now() - 600 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
// Mix of online (working) + offline.
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
38
|
+
{ alias: 'on-a', status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
40
|
+
{ alias: 'on-b', status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
42
|
+
{ alias: 'off-c', status: 'offline', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
43
|
+
network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
// Trigger zoom-in to mount minimap (R30 gate).
|
|
52
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
53
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
54
|
+
await page.waitForTimeout(200);
|
|
55
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
56
|
+
await page.waitForSelector('[data-topo-minimap-dot]', { timeout: 5000 });
|
|
57
|
+
await page.waitForTimeout(400);
|
|
58
|
+
|
|
59
|
+
const probe = await page.evaluate(() => {
|
|
60
|
+
const online = document.querySelector('[data-topo-minimap-dot-online="true"]');
|
|
61
|
+
const offline = document.querySelector('[data-topo-minimap-dot-online="false"]');
|
|
62
|
+
return {
|
|
63
|
+
onlineR: online?.getAttribute('r') ?? null,
|
|
64
|
+
onlineRData: online?.getAttribute('data-topo-minimap-dot-radius') ?? null,
|
|
65
|
+
onlineOpacity: online?.getAttribute('opacity') ?? null,
|
|
66
|
+
offlineR: offline?.getAttribute('r') ?? null,
|
|
67
|
+
offlineRData: offline?.getAttribute('data-topo-minimap-dot-radius') ?? null,
|
|
68
|
+
offlineOpacity: offline?.getAttribute('opacity') ?? null,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
online_r_1_9: probe.onlineR === '1.9',
|
|
76
|
+
online_r_data_1_9: probe.onlineRData === '1.9',
|
|
77
|
+
online_opacity_0_9: probe.onlineOpacity === '0.9', // R198 invariant
|
|
78
|
+
offline_r_1_2: probe.offlineR === '1.2', // R198 invariant
|
|
79
|
+
offline_r_data_1_2: probe.offlineRData === '1.2',
|
|
80
|
+
offline_opacity_0_6: probe.offlineOpacity === '0.6', // R372 lift
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} minimap online dot radius 1.7 → 1.9:`, JSON.stringify(results),
|
|
84
|
+
'\n probe:', probe);
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* Round 379 verification: minimap viewport rect picks up
|
|
2
|
+
* strokeLinejoin='round'. Sibling SVG stroke-softening polish to
|
|
3
|
+
* R378 flow-rail linecap + R288 chrome icon linecap family.
|
|
4
|
+
*
|
|
5
|
+
* The minimap only mounts when view is non-default (zoom !== 1 ||
|
|
6
|
+
* pan), so test triggers zoom-in to mount it.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - [data-topo-minimap-viewport] element stroke-linejoin attr === 'round'.
|
|
10
|
+
* - data-topo-minimap-viewport-linejoin === 'round'.
|
|
11
|
+
* - Pre-R379 invariants:
|
|
12
|
+
* * R287 strokeWidth=1.5 (cold rest)
|
|
13
|
+
* * R287 stroke=pal.legendAccent
|
|
14
|
+
* * R346 opacity=0.9 (cold rest)
|
|
15
|
+
* * R199 smoothView + R346 transition list still tween correctly
|
|
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
|
+
// Trigger zoom-in to mount minimap (R30 gate).
|
|
45
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
46
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
47
|
+
await page.waitForTimeout(200);
|
|
48
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
49
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
50
|
+
await page.waitForTimeout(400);
|
|
51
|
+
|
|
52
|
+
const probe = await page.evaluate(() => {
|
|
53
|
+
const r = document.querySelector('[data-topo-minimap-viewport]');
|
|
54
|
+
return {
|
|
55
|
+
linejoinAttr: r?.getAttribute('stroke-linejoin') ?? null,
|
|
56
|
+
linejoinData: r?.getAttribute('data-topo-minimap-viewport-linejoin') ?? null,
|
|
57
|
+
strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
|
|
58
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
59
|
+
fillAttr: r?.getAttribute('fill') ?? null,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await browser.close();
|
|
64
|
+
|
|
65
|
+
const results = {
|
|
66
|
+
linejoin_round: probe.linejoinAttr === 'round',
|
|
67
|
+
data_linejoin_round: probe.linejoinData === 'round',
|
|
68
|
+
stroke_width_1_5_rest: probe.strokeWidthAttr === '1.5', // R287 cold rest
|
|
69
|
+
opacity_0_9_rest: probe.opacityAttr === '0.9', // R346 cold rest
|
|
70
|
+
fill_none: probe.fillAttr === 'none', // viewport rect is stroke-only
|
|
71
|
+
};
|
|
72
|
+
const ok = Object.values(results).every(Boolean);
|
|
73
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport linejoin='round':`, JSON.stringify(results),
|
|
74
|
+
'\n probe:', probe);
|
|
75
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 393 verification: minimap viewport rect rx 0 → 2.
|
|
2
|
+
* Corner-radius cascade family adds a sub-element tier (7th
|
|
3
|
+
* anchor) by softening the cyan-stroked viewport frame inside
|
|
4
|
+
* the R332 rounded minimap container. Small but visible polish
|
|
5
|
+
* — 2-px radius reads as "not sharp" without feeling pillowy on
|
|
6
|
+
* the 30-50px wide viewport rect.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - Minimap only mounts on non-default view → zoom-in twice
|
|
10
|
+
* - viewport rect rx attr === '2'
|
|
11
|
+
* - data-topo-minimap-viewport-rx === '2'
|
|
12
|
+
* - Pre-R393 invariants preserved:
|
|
13
|
+
* * R346 hover-state strokeWidth tween (rest '1.5' / hover '1.75')
|
|
14
|
+
* * R346 opacity tween (rest '0.9' / hover '1')
|
|
15
|
+
* * R379 strokeLinejoin='round'
|
|
16
|
+
* * fill='none' + stroke=pal.legendAccent
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
23
|
+
|
|
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
|
+
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: 'idle', 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('alpha'), mk('beta'), mk('gamma') ] } });
|
|
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('g[data-node]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
50
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
51
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const probe = await page.evaluate(() => {
|
|
55
|
+
const v = document.querySelector('[data-topo-minimap-viewport]');
|
|
56
|
+
if (!v) return null;
|
|
57
|
+
return {
|
|
58
|
+
rxAttr: v.getAttribute('rx'),
|
|
59
|
+
rxData: v.getAttribute('data-topo-minimap-viewport-rx'),
|
|
60
|
+
strokeWidth: v.getAttribute('stroke-width'),
|
|
61
|
+
opacity: v.getAttribute('opacity'),
|
|
62
|
+
linejoin: v.getAttribute('stroke-linejoin'),
|
|
63
|
+
fill: v.getAttribute('fill'),
|
|
64
|
+
strokePresent: !!v.getAttribute('stroke') && v.getAttribute('stroke') !== 'none',
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
// R393: rx 0 → 2
|
|
72
|
+
rx_attr_2: probe?.rxAttr === '2',
|
|
73
|
+
rx_data_2: probe?.rxData === '2',
|
|
74
|
+
// R346 hover-state rest invariants (no hover yet → rest values)
|
|
75
|
+
rest_strokeWidth_15: probe?.strokeWidth === '1.5',
|
|
76
|
+
rest_opacity_09: probe?.opacity === '0.9',
|
|
77
|
+
// R379 + base invariants
|
|
78
|
+
linejoin_round: probe?.linejoin === 'round',
|
|
79
|
+
fill_none: probe?.fill === 'none',
|
|
80
|
+
stroke_present: probe?.strokePresent === true,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport rect rx 0 → 2:`, JSON.stringify(results),
|
|
84
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 368 verification: recent-panel '+N more flows' footer text
|
|
2
|
+
* fontWeight 400 → 500. Sibling small-text fw lift family with R363
|
|
3
|
+
* recent-row alias + R364 legend-row label + R366 group-label count
|
|
4
|
+
* — all four lifts thicken small-fontSize text (9-11 px) against
|
|
5
|
+
* panel chrome from SVG-default 400 to the font-medium tier (500).
|
|
6
|
+
*
|
|
7
|
+
* The footer only renders when flowLinks.length > 3 (the panel shows
|
|
8
|
+
* the 3 most recent + a footer link to /messages for the overflow).
|
|
9
|
+
*
|
|
10
|
+
* Contract (cyber, 5+ flow links so the footer mounts):
|
|
11
|
+
* - data-recent-panel-more text element font-weight === '500'.
|
|
12
|
+
* - data-recent-panel-more-font-weight === '500'.
|
|
13
|
+
* - Pre-R368 invariants on the same element:
|
|
14
|
+
* * fontSize='9' + fontFamily='monospace' + fontStyle='italic'
|
|
15
|
+
* * letter-spacing='0.2' (R325 rest baseline)
|
|
16
|
+
* * opacity='0.55' (rest, R325 era)
|
|
17
|
+
* * inline transition lists opacity + letter-spacing + fill
|
|
18
|
+
* * data-recent-panel-more attr surfaces the count
|
|
19
|
+
* * data-recent-panel-more-unit tspan with opacity 0.7 (R340)
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('a'), mk('b'), mk('c'), mk('d'), mk('e'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
// 5 distinct flow links → panel shows 3 + footer "+ 2 more flows".
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const pairs = [['a','b'], ['c','d'], ['a','c'], ['b','d'], ['e','a']];
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages:
|
|
49
|
+
pairs.map(([from, to], idx) => ({
|
|
50
|
+
id: `m${idx}`, from_alias: from, to_alias: to, content: `ping ${idx}`,
|
|
51
|
+
network_id: 'default',
|
|
52
|
+
created_at: new Date(now - (5000 + idx * 1000)).toISOString(),
|
|
53
|
+
}))
|
|
54
|
+
} }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
56
|
+
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const t = document.querySelector('[data-recent-panel-more]');
|
|
64
|
+
const unit = document.querySelector('[data-recent-panel-more-unit]');
|
|
65
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
66
|
+
return {
|
|
67
|
+
fontWeightAttr: t?.getAttribute('font-weight') ?? null,
|
|
68
|
+
fontWeightData: t?.getAttribute('data-recent-panel-more-font-weight') ?? null,
|
|
69
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
70
|
+
fontFamily: t?.getAttribute('font-family') ?? null,
|
|
71
|
+
fontStyle: t?.getAttribute('font-style') ?? null,
|
|
72
|
+
letterSpacing: t?.getAttribute('letter-spacing') ?? null,
|
|
73
|
+
opacityAttr: t?.getAttribute('opacity') ?? null,
|
|
74
|
+
moreCount: t?.getAttribute('data-recent-panel-more') ?? null,
|
|
75
|
+
hoveredAttr: t?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
76
|
+
transition: cs?.transition ?? null,
|
|
77
|
+
unitOpacity: unit?.getAttribute('opacity') ?? null,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await browser.close();
|
|
82
|
+
|
|
83
|
+
const hasTrans = (s, prop) =>
|
|
84
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
85
|
+
|
|
86
|
+
const results = {
|
|
87
|
+
font_weight_500: probe.fontWeightAttr === '500',
|
|
88
|
+
data_fw_500: probe.fontWeightData === '500',
|
|
89
|
+
font_size_9: probe.fontSizeAttr === '9',
|
|
90
|
+
font_family_mono: /monospace/i.test(probe.fontFamily || ''),
|
|
91
|
+
font_style_italic: probe.fontStyle === 'italic', // pre-R368
|
|
92
|
+
letter_spacing_0_2: probe.letterSpacing === '0.2', // R325 rest
|
|
93
|
+
opacity_0_55_rest: probe.opacityAttr === '0.55', // R325 era rest
|
|
94
|
+
more_count_2: probe.moreCount === '2', // 5 links - 3 shown
|
|
95
|
+
hovered_false_rest: probe.hoveredAttr === 'false',
|
|
96
|
+
trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'),
|
|
97
|
+
trans_has_fill: hasTrans(probe.transition, 'fill'),
|
|
98
|
+
unit_opacity_0_7: probe.unitOpacity === '0.7', // R340 unit split
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} more-flows footer fw 400 → 500:`, JSON.stringify(results),
|
|
102
|
+
'\n probe:', probe);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Round 427 verification: node alias label letter-spacing — 3-tier
|
|
2
|
+
* scale rest 0 / hover 0.3 / chat-target 0.5. Extends the hover-
|
|
3
|
+
* letter-spacing family (R344/R345/R347/R351/R420) to per-node alias
|
|
4
|
+
* scope.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - rest: every visible alias-text reports letter-spacing 0px
|
|
8
|
+
* (resolves to keyword 'normal' in computed style on some engines —
|
|
9
|
+
* R218 trap; accept either form)
|
|
10
|
+
* - hover a node: that node's alias-text reports letter-spacing 0.3px,
|
|
11
|
+
* all others stay at rest 0
|
|
12
|
+
* - source-file probe confirms the 3-tier conditional + transition list
|
|
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: 1500 } });
|
|
22
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
23
|
+
await ctx.addInitScript(() => {
|
|
24
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
25
|
+
});
|
|
26
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
27
|
+
const r = await route.fetch();
|
|
28
|
+
const b = await r.json();
|
|
29
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
30
|
+
const mk = (alias, status) => ({
|
|
31
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
32
|
+
network_id: nid, project_dir: null,
|
|
33
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
34
|
+
});
|
|
35
|
+
// small fleet (3 nodes) so labels are in card-mode, not dense
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
37
|
+
mk('alpha', 'working'),
|
|
38
|
+
mk('beta', 'idle'),
|
|
39
|
+
mk('gamma', 'working'),
|
|
40
|
+
] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('[data-node-alias-text]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const readAll = () => page.evaluate(() => {
|
|
51
|
+
const ts = [...document.querySelectorAll('[data-node-alias-text]')];
|
|
52
|
+
return ts.map(t => ({
|
|
53
|
+
alias: t.getAttribute('data-node-alias-text'),
|
|
54
|
+
ls: t.style.letterSpacing, // inline style
|
|
55
|
+
ls_c: getComputedStyle(t).letterSpacing, // computed (may be 'normal')
|
|
56
|
+
chat: t.getAttribute('data-node-alias-chat-target'),
|
|
57
|
+
hover: t.getAttribute('data-node-alias-hovered'),
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const rest = await readAll();
|
|
62
|
+
|
|
63
|
+
// Hover the first node's group (its bounding rect)
|
|
64
|
+
const firstAlias = rest[0]?.alias;
|
|
65
|
+
let hover = null;
|
|
66
|
+
if (firstAlias) {
|
|
67
|
+
const box = await page.evaluate((alias) => {
|
|
68
|
+
const t = document.querySelector(`[data-node-alias-text="${alias}"]`);
|
|
69
|
+
if (!t) return null;
|
|
70
|
+
// Walk up to the node group (data-node) for the group-hover hit.
|
|
71
|
+
const node = t.closest('[data-node]');
|
|
72
|
+
const target = node || t;
|
|
73
|
+
const b = target.getBoundingClientRect();
|
|
74
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
75
|
+
}, firstAlias);
|
|
76
|
+
if (box) {
|
|
77
|
+
await page.mouse.move(box.x, box.y);
|
|
78
|
+
await page.waitForTimeout(300);
|
|
79
|
+
hover = await readAll();
|
|
80
|
+
await page.mouse.move(0, 0);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Source-file probe
|
|
85
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
86
|
+
const sourceWired = /chatAlias\s*===\s*session\.alias\s*\?\s*'0\.5px'\s*:\s*\n\s*hoveredAlias\s*===\s*session\.alias\s*\?\s*'0\.3px'\s*:\s*'0px'/.test(fileText);
|
|
87
|
+
const sourceTransition = /transition: 'fill 300ms ease-out, letter-spacing 200ms ease-out'/.test(fileText);
|
|
88
|
+
|
|
89
|
+
await browser.close();
|
|
90
|
+
|
|
91
|
+
const isRest = (ls) => ls === '0px' || ls === '' || ls === 'normal';
|
|
92
|
+
const isMid = (ls) => ls === '0.3px';
|
|
93
|
+
|
|
94
|
+
const restAllZero = rest.length > 0 && rest.every(r => isRest(r.ls));
|
|
95
|
+
const hoveredEntry = hover?.find(r => r.alias === firstAlias);
|
|
96
|
+
const othersStillRest = hover ? hover.filter(r => r.alias !== firstAlias).every(r => isRest(r.ls)) : false;
|
|
97
|
+
|
|
98
|
+
const results = {
|
|
99
|
+
rest_mounted_count_gte_3: rest.length >= 3,
|
|
100
|
+
rest_chat_all_false: rest.every(r => r.chat === 'false'),
|
|
101
|
+
rest_hover_all_false: rest.every(r => r.hover === 'false'),
|
|
102
|
+
rest_letter_spacing_zero: restAllZero,
|
|
103
|
+
hover_target_ls_0_3: isMid(hoveredEntry?.ls || ''),
|
|
104
|
+
hover_target_hover_attr: hoveredEntry?.hover === 'true',
|
|
105
|
+
hover_others_stay_rest: othersStillRest,
|
|
106
|
+
source_three_tier_wired: sourceWired,
|
|
107
|
+
source_transition_kept: sourceTransition,
|
|
108
|
+
};
|
|
109
|
+
const ok = Object.values(results).every(Boolean);
|
|
110
|
+
console.log(`${ok ? '✅' : '❌'} node alias letter-spacing 3-tier:`, JSON.stringify(results),
|
|
111
|
+
'\n rest:', JSON.stringify(rest), '\n hover:', JSON.stringify(hover));
|
|
112
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 407 verification: node halo offline opacity lift — cyber
|
|
2
|
+
* 0.25 → 0.30 and light 0.4 → 0.45. Probes both themes via two
|
|
3
|
+
* browser contexts with a single offline (ghost) session.
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Online node (status: idle): opacity '0.65' cyber / '0.85' light
|
|
7
|
+
* (unchanged by R407 — invariant probe)
|
|
8
|
+
* - Offline node (status: offline + stale last_seen_at):
|
|
9
|
+
* * cyber: opacity attr === '0.3'
|
|
10
|
+
* * light: opacity attr === '0.45'
|
|
11
|
+
* * data-node-halo-offline-opacity === '0.3' / '0.45'
|
|
12
|
+
* - Source-file verification: the conditional
|
|
13
|
+
* `(isLight ? 0.45 : 0.30)` is present.
|
|
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
|
+
const stale = new Date(Date.now() - 30 * 60 * 1000).toISOString();
|
|
21
|
+
|
|
22
|
+
async function probeTheme(theme) {
|
|
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((t) => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', t); sessionStorage.setItem('anet_v3_auth', '1'); } 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
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
34
|
+
{ alias: 'alpha', status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
35
|
+
{ alias: 'ghost', status: 'offline', model: 'claude-opus-4', runtime: 'claude-code-cli', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
36
|
+
] } });
|
|
37
|
+
});
|
|
38
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
39
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
40
|
+
const page = await ctx.newPage();
|
|
41
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
42
|
+
await page.waitForSelector('g[data-node]', { timeout: 15000 });
|
|
43
|
+
await page.waitForTimeout(400);
|
|
44
|
+
const probe = await page.evaluate(() => {
|
|
45
|
+
const nodes = Array.from(document.querySelectorAll('g[data-node]'));
|
|
46
|
+
const read = (alias) => {
|
|
47
|
+
const node = nodes.find((n) => n.getAttribute('data-node') === alias);
|
|
48
|
+
if (!node) return null;
|
|
49
|
+
const halo = node.querySelector('[data-node-halo-breath]');
|
|
50
|
+
return halo ? {
|
|
51
|
+
opacityAttr: halo.getAttribute('opacity'),
|
|
52
|
+
offlineData: halo.getAttribute('data-node-halo-offline-opacity'),
|
|
53
|
+
} : null;
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
online: read('alpha'),
|
|
57
|
+
offline: read('ghost'),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
await browser.close();
|
|
61
|
+
return probe;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cyber = await probeTheme('cyber');
|
|
65
|
+
const light = await probeTheme('light');
|
|
66
|
+
|
|
67
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
68
|
+
const sourceHasOfflineLift = /isOnline\s*\?\s*\(isLight\s*\?\s*0\.85\s*:\s*0\.65\)\s*:\s*\(isLight\s*\?\s*0\.45\s*:\s*0\.30\)/.test(fileText);
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
// Online (R407 doesn't touch online — invariant)
|
|
72
|
+
cyber_online_065: cyber?.online?.opacityAttr === '0.65',
|
|
73
|
+
light_online_085: light?.online?.opacityAttr === '0.85',
|
|
74
|
+
cyber_online_no_offline_attr: cyber?.online?.offlineData === null || cyber?.online?.offlineData === undefined,
|
|
75
|
+
// Offline (R407 lift)
|
|
76
|
+
cyber_offline_0_3: cyber?.offline?.opacityAttr === '0.3',
|
|
77
|
+
cyber_offline_data_0_3: cyber?.offline?.offlineData === '0.3',
|
|
78
|
+
light_offline_0_45: light?.offline?.opacityAttr === '0.45',
|
|
79
|
+
light_offline_data_0_45: light?.offline?.offlineData === '0.45',
|
|
80
|
+
// Source-file verification
|
|
81
|
+
source_offline_lift: sourceHasOfflineLift,
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} node halo offline opacity lift:`, JSON.stringify(results),
|
|
85
|
+
'\n cyber:', cyber,
|
|
86
|
+
'\n light:', light);
|
|
87
|
+
process.exit(ok ? 0 : 1);
|