@sleep2agi/agent-network-dashboard 0.5.1-preview.13 → 0.5.1-preview.130
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 +7 -7
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-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.js.nft.json +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/{[root-of-the-server]__03b.f76._.js → [root-of-the-server]__0sv~g.o._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -0
- 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/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0-at-u3ty_q5z.js +1 -0
- package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
- package/.next/static/chunks/0kz7356-vy97a.js +1 -0
- package/.next/static/chunks/{03~~oirxz7~vc.js → 0phbmewz8hp6a.js} +1 -1
- package/.next/static/chunks/11xegdodzgys0.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +2528 -186
- package/package.json +1 -1
- package/screenshots/v0.11.0-147/after.png +0 -0
- package/scripts/p0-147-screenshot.mjs +53 -0
- 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-fullscreen-icon-sw-test.mjs +92 -0
- package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
- package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
- package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
- package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
- package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
- package/scripts/topo-dense-alias-opacity-test.mjs +68 -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-letter-spacing-test.mjs +86 -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-endpoint-hover-sw-test.mjs +127 -0
- package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
- package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
- package/scripts/topo-edge-particle-radius-test.mjs +76 -0
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
- package/scripts/topo-endpoint-ring-r-hover-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-hover-sw-test.mjs +113 -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-geom-transition-test.mjs +110 -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-group-label-count-fw-test.mjs +100 -0
- package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
- package/scripts/topo-group-label-letter-spacing-test.mjs +113 -0
- package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
- package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
- package/scripts/topo-group-pip-fontsize-test.mjs +106 -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-core-fill-hover-test.mjs +85 -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-r-hover-test.mjs +82 -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-hover-sw-test.mjs +121 -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-radius-test.mjs +87 -0
- package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
- package/scripts/topo-legend-count-pin-fw-test.mjs +90 -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-legend-row-letter-spacing-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-opacity-test.mjs +96 -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-hover-opacity-test.mjs +104 -0
- package/scripts/topo-node-halo-light-offline-test.mjs +80 -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-fw-test.mjs +75 -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-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-count-pin-fw-test.mjs +106 -0
- package/scripts/topo-recent-row-letter-spacing-test.mjs +109 -0
- package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
- package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
- package/scripts/topo-runtime-icon-hover-test.mjs +96 -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-status-ring-hover-sw-test.mjs +105 -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-level-hover-fw-test.mjs +95 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
- package/.next/static/chunks/0is7s1ik8dy24.js +0 -4
- package/.next/static/chunks/0kt7qs66ra2.2.css +0 -2
- package/.next/static/chunks/0yl__lz0pv5_7.js +0 -1
- package/.next/static/chunks/1090s4vyw-jrc.js +0 -1
- /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_buildManifest.js +0 -0
- /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Round 447 verification: recent-row freshness pip radius hover lift —
|
|
2
|
+
* r 2.0 → 2.5 on (isRowHovered || isRowPinned).
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* - rest: every pip reports data-recent-row-freshness-radius '2' +
|
|
6
|
+
* lifted='false'
|
|
7
|
+
* - hover one row: that row's pip lifts to '2.5' + lifted='true'
|
|
8
|
+
* - siblings stay rest
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
15
|
+
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
21
|
+
});
|
|
22
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
23
|
+
const r = await route.fetch();
|
|
24
|
+
const b = await r.json();
|
|
25
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
26
|
+
const mk = (alias, status) => ({
|
|
27
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
28
|
+
network_id: nid, project_dir: null,
|
|
29
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
30
|
+
});
|
|
31
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
32
|
+
mk('alpha', 'working'),
|
|
33
|
+
mk('beta', 'idle'),
|
|
34
|
+
mk('gamma', 'idle'),
|
|
35
|
+
] } });
|
|
36
|
+
});
|
|
37
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
38
|
+
json: { messages: [
|
|
39
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
40
|
+
{ id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
|
|
41
|
+
] },
|
|
42
|
+
}));
|
|
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-recent-row-freshness]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const readAll = () => page.evaluate(() => {
|
|
51
|
+
const ps = [...document.querySelectorAll('[data-recent-row-freshness]')];
|
|
52
|
+
return ps.map(p => ({
|
|
53
|
+
key: p.getAttribute('data-recent-row-freshness'),
|
|
54
|
+
r: parseFloat(p.getAttribute('data-recent-row-freshness-radius') || '0'),
|
|
55
|
+
lifted: p.getAttribute('data-recent-row-freshness-lifted'),
|
|
56
|
+
}));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const rest = await readAll();
|
|
60
|
+
const firstKey = rest[0]?.key;
|
|
61
|
+
|
|
62
|
+
// Hover the corresponding data-recent-row wrapper
|
|
63
|
+
let hover = null;
|
|
64
|
+
if (firstKey) {
|
|
65
|
+
const box = await page.evaluate((key) => {
|
|
66
|
+
const g = document.querySelector(`[data-recent-row="${key}"]`);
|
|
67
|
+
if (!g) return null;
|
|
68
|
+
const b = g.getBoundingClientRect();
|
|
69
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
70
|
+
}, firstKey);
|
|
71
|
+
if (box) {
|
|
72
|
+
await page.mouse.move(box.x, box.y);
|
|
73
|
+
await page.waitForTimeout(300);
|
|
74
|
+
hover = await readAll();
|
|
75
|
+
await page.mouse.move(0, 0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
80
|
+
const sourceWired = /r:\s*`\$\{\(isRowHovered \|\| isRowPinned\) \? 2\.5 : 2\.0\}px`/.test(fileText);
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const restAll_2_0 = rest.every(r => r.r === 2.0);
|
|
85
|
+
const restNoLifted = rest.every(r => r.lifted === 'false');
|
|
86
|
+
const hoveredEntry = hover?.find(r => r.key === firstKey);
|
|
87
|
+
const hoverR_2_5 = hoveredEntry?.r === 2.5;
|
|
88
|
+
const hoverLifted = hoveredEntry?.lifted === 'true';
|
|
89
|
+
const othersStayRest = hover ? hover.filter(r => r.key !== firstKey).every(r => r.r === 2.0 && r.lifted === 'false') : false;
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
rest_count_ge_2: rest.length >= 2,
|
|
93
|
+
rest_all_r_2_0: restAll_2_0,
|
|
94
|
+
rest_no_lifted: restNoLifted,
|
|
95
|
+
hover_target_r_2_5: hoverR_2_5,
|
|
96
|
+
hover_target_lifted: hoverLifted,
|
|
97
|
+
hover_others_stay: othersStayRest,
|
|
98
|
+
source_wired: sourceWired,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} recent-row pip hover r:`, JSON.stringify(results),
|
|
102
|
+
'\n rest:', JSON.stringify(rest),
|
|
103
|
+
'\n hover target:', JSON.stringify(hoveredEntry));
|
|
104
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 363 verification: recent-signal row text fontWeight 400 → 500.
|
|
2
|
+
* At fontSize=9 the default-weight text was thin; R363 lifts to the
|
|
3
|
+
* font-medium tier (500) for better legibility against the panel
|
|
4
|
+
* chrome. R320 count tspan fw 600/700 override stays local, so the
|
|
5
|
+
* count-vs-alias hierarchy holds:
|
|
6
|
+
* alias fw 500 (R363)
|
|
7
|
+
* count fw 600/700 (R320)
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - data-recent-row-text element font-weight attr === '500'.
|
|
11
|
+
* - data-recent-row-text-font-weight === '500'.
|
|
12
|
+
* - The inner count tspan still has fw 600 (cold flow, R320 default).
|
|
13
|
+
* - Pre-R363 invariants:
|
|
14
|
+
* * fontSize=9 + fontFamily=monospace
|
|
15
|
+
* * R220 letter-spacing pin tween still wired (style.transition
|
|
16
|
+
* contains letter-spacing)
|
|
17
|
+
* * data-recent-row-text key surfaces the link key
|
|
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') ] } });
|
|
40
|
+
});
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
// 3 messages on the same link → count = 3 (cold, below the 10 hot threshold).
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
44
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
45
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
46
|
+
{ id: 'm2', from_alias: 'a', to_alias: 'b', content: 'pong',
|
|
47
|
+
network_id: 'default', created_at: new Date(now - 4000).toISOString() },
|
|
48
|
+
{ id: 'm3', from_alias: 'a', to_alias: 'b', content: 'ping2',
|
|
49
|
+
network_id: 'default', created_at: new Date(now - 3000).toISOString() },
|
|
50
|
+
] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForSelector('[data-recent-row-text]', { timeout: 15000 });
|
|
56
|
+
await page.waitForTimeout(300);
|
|
57
|
+
|
|
58
|
+
const probe = await page.evaluate(() => {
|
|
59
|
+
const t = document.querySelector('[data-recent-row-text]');
|
|
60
|
+
const count = document.querySelector('[data-recent-row-count]');
|
|
61
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
62
|
+
return {
|
|
63
|
+
fontWeightAttr: t?.getAttribute('font-weight') ?? null,
|
|
64
|
+
fontWeightData: t?.getAttribute('data-recent-row-text-font-weight') ?? null,
|
|
65
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
66
|
+
fontFamily: t?.getAttribute('font-family') ?? null,
|
|
67
|
+
linkKey: t?.getAttribute('data-recent-row-text') ?? null,
|
|
68
|
+
transition: cs?.transition ?? null,
|
|
69
|
+
countFwAttr: count?.getAttribute('font-weight') ?? null, // R320 override
|
|
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
|
+
font_weight_500: probe.fontWeightAttr === '500',
|
|
80
|
+
data_fw_500: probe.fontWeightData === '500',
|
|
81
|
+
font_size_9: probe.fontSizeAttr === '9',
|
|
82
|
+
font_family_mono: /monospace/i.test(probe.fontFamily || ''),
|
|
83
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
84
|
+
trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'), // R220
|
|
85
|
+
count_fw_600: probe.countFwAttr === '600', // R320 cold default
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} recent-row text fw 400 → 500:`, JSON.stringify(results),
|
|
89
|
+
'\n probe:', probe);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* Round 443 verification: runtime badge inner-icon strokeWidth hover
|
|
2
|
+
* lift — 2.4 → 2.8 on node hover. Sibling to R208 outer ring sw lift.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* - rest: every runtime icon path reports stroke-width '2.4' +
|
|
6
|
+
* active='false'
|
|
7
|
+
* - hover one node: that icon path stroke-width '2.8' + active='true'
|
|
8
|
+
* - siblings stay rest
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
15
|
+
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
21
|
+
});
|
|
22
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
23
|
+
const r = await route.fetch();
|
|
24
|
+
const b = await r.json();
|
|
25
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
26
|
+
const mk = (alias, status, runtime) => ({
|
|
27
|
+
alias, status, model: 'claude-opus-4', runtime,
|
|
28
|
+
network_id: nid, project_dir: null,
|
|
29
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
30
|
+
});
|
|
31
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
32
|
+
mk('alpha', 'working', 'claude-code-cli'),
|
|
33
|
+
mk('beta', 'idle', 'claude-code-cli'),
|
|
34
|
+
mk('gamma', 'working', 'claude-code-cli'),
|
|
35
|
+
] } });
|
|
36
|
+
});
|
|
37
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
38
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
39
|
+
|
|
40
|
+
const page = await ctx.newPage();
|
|
41
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
42
|
+
await page.waitForSelector('[data-runtime-badge-icon]', { timeout: 15000 });
|
|
43
|
+
await page.waitForTimeout(400);
|
|
44
|
+
|
|
45
|
+
const readAll = () => page.evaluate(() => {
|
|
46
|
+
const ps = [...document.querySelectorAll('[data-runtime-badge-icon]')];
|
|
47
|
+
return ps.map(p => ({
|
|
48
|
+
alias: p.getAttribute('data-runtime-badge-icon'),
|
|
49
|
+
sw: p.getAttribute('stroke-width'),
|
|
50
|
+
active: p.getAttribute('data-runtime-badge-icon-active'),
|
|
51
|
+
}));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const rest = await readAll();
|
|
55
|
+
let hover = null;
|
|
56
|
+
const box = await page.evaluate(() => {
|
|
57
|
+
const t = document.querySelector('[data-node-alias-text="alpha"]');
|
|
58
|
+
if (!t) return null;
|
|
59
|
+
const node = t.closest('[data-node]');
|
|
60
|
+
const target = node || t;
|
|
61
|
+
const b = target.getBoundingClientRect();
|
|
62
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
63
|
+
});
|
|
64
|
+
if (box) {
|
|
65
|
+
await page.mouse.move(box.x, box.y);
|
|
66
|
+
await page.waitForTimeout(300);
|
|
67
|
+
hover = await readAll();
|
|
68
|
+
await page.mouse.move(0, 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
72
|
+
const sourceWired = /strokeWidth=\{isNodeActive \? '2\.8' : '2\.4'\}/.test(fileText);
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const restAll_2_4 = rest.every(r => r.sw === '2.4');
|
|
77
|
+
const restNoActive = rest.every(r => r.active === 'false');
|
|
78
|
+
const hoveredEntry = hover?.find(r => r.alias === 'alpha');
|
|
79
|
+
const hoverSw_2_8 = hoveredEntry?.sw === '2.8';
|
|
80
|
+
const hoverActive = hoveredEntry?.active === 'true';
|
|
81
|
+
const othersRest = hover ? hover.filter(r => r.alias !== 'alpha').every(r => r.sw === '2.4' && r.active === 'false') : false;
|
|
82
|
+
|
|
83
|
+
const results = {
|
|
84
|
+
rest_count_ge_3: rest.length >= 3,
|
|
85
|
+
rest_all_sw_2_4: restAll_2_4,
|
|
86
|
+
rest_all_active_false: restNoActive,
|
|
87
|
+
hover_target_sw_2_8: hoverSw_2_8,
|
|
88
|
+
hover_target_active: hoverActive,
|
|
89
|
+
hover_others_stay: othersRest,
|
|
90
|
+
source_wired: sourceWired,
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} runtime icon hover sw:`, JSON.stringify(results),
|
|
94
|
+
'\n rest:', JSON.stringify(rest),
|
|
95
|
+
'\n hover target:', JSON.stringify(hoveredEntry));
|
|
96
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Round 391 verification: hub-spoke active opacity 0.7 → 0.8. An
|
|
2
|
+
* "active" spoke is one whose alias has recent message traffic
|
|
3
|
+
* (activeAliases is populated from buildFlowLinks(messages)). The
|
|
4
|
+
* lift unifies active-spoke alpha with R370 hub hover-ring opacity
|
|
5
|
+
* 0.7 → 0.8 cyber — paired canvas signals at matching alpha.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - When the alias appears as flow-link from/to, its spoke has:
|
|
9
|
+
* * opacity attr === '0.8'
|
|
10
|
+
* * data-topo-hub-spoke-opacity === '0.8'
|
|
11
|
+
* * data-topo-hub-spoke-active === 'true'
|
|
12
|
+
* - Idle spokes (no flow traffic) unchanged:
|
|
13
|
+
* * opacity attr === '0.45'
|
|
14
|
+
* * data-topo-hub-spoke-active === 'false'
|
|
15
|
+
* - Pre-R391 invariants preserved on the active spoke:
|
|
16
|
+
* * R382 strokeLinecap='round'
|
|
17
|
+
* * R51-safe strokeWidth=2 (active)
|
|
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
|
|
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
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
|
|
41
|
+
});
|
|
42
|
+
// Seed a flow link between alpha and beta so their spokes become
|
|
43
|
+
// active; gamma stays idle. activeAliases = { alpha, beta }.
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
45
|
+
json: {
|
|
46
|
+
messages: [
|
|
47
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
48
|
+
{ id: 'm2', from_alias: 'alpha', to_alias: 'beta', content: 'pong', created_at: fresh, network_id: 'default' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
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('g[data-node]', { timeout: 15000 });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
// Wait for the dashboard to ingest the seeded messages and recompute
|
|
60
|
+
// activeAliases. The flow-links derivation runs in a useMemo that
|
|
61
|
+
// depends on `messages`; allow a tick for SWR/state propagation.
|
|
62
|
+
await page.waitForTimeout(800);
|
|
63
|
+
|
|
64
|
+
const probe = await page.evaluate(() => {
|
|
65
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
66
|
+
const active = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'true');
|
|
67
|
+
const idle = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'false');
|
|
68
|
+
const read = (el) => el ? ({
|
|
69
|
+
opacityAttr: el.getAttribute('opacity'),
|
|
70
|
+
opacityData: el.getAttribute('data-topo-hub-spoke-opacity'),
|
|
71
|
+
strokeWidth: el.getAttribute('stroke-width'),
|
|
72
|
+
linecap: el.getAttribute('stroke-linecap'),
|
|
73
|
+
}) : null;
|
|
74
|
+
return {
|
|
75
|
+
total: spokes.length,
|
|
76
|
+
activeCount: active.length,
|
|
77
|
+
idleCount: idle.length,
|
|
78
|
+
activeSample: read(active[0]),
|
|
79
|
+
idleSample: read(idle[0]),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const results = {
|
|
86
|
+
// 3 spokes total (alpha, beta, gamma)
|
|
87
|
+
total_3_spokes: probe.total === 3,
|
|
88
|
+
// alpha + beta active (flow link)
|
|
89
|
+
active_2_spokes: probe.activeCount === 2,
|
|
90
|
+
idle_1_spoke: probe.idleCount === 1,
|
|
91
|
+
// R391: active opacity '0.8' (was '0.7')
|
|
92
|
+
active_opacity_0_8: probe.activeSample?.opacityAttr === '0.8',
|
|
93
|
+
active_data_0_8: probe.activeSample?.opacityData === '0.8',
|
|
94
|
+
// Idle invariant
|
|
95
|
+
idle_opacity_0_45: probe.idleSample?.opacityAttr === '0.45',
|
|
96
|
+
idle_data_0_45: probe.idleSample?.opacityData === '0.45',
|
|
97
|
+
// R382 + R51-safe invariants on active spoke
|
|
98
|
+
active_linecap_round: probe.activeSample?.linecap === 'round',
|
|
99
|
+
active_strokeWidth_2: probe.activeSample?.strokeWidth === '2',
|
|
100
|
+
};
|
|
101
|
+
const ok = Object.values(results).every(Boolean);
|
|
102
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke active opacity 0.7 → 0.8:`, JSON.stringify(results),
|
|
103
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
104
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 415 verification: hub-spoke active strokeWidth 2 → 2.25.
|
|
2
|
+
* Visual-weight bump family 14th anchor — active-spoke stroke
|
|
3
|
+
* thickens to match its role as the focal connection. Pairs with
|
|
4
|
+
* R391 opacity 0.7 → 0.8 so the same active path lifts both
|
|
5
|
+
* stroke AND opacity in concert.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Seed a flow link so a node becomes active (R243 isActive gate)
|
|
9
|
+
* - The active spoke <path>:
|
|
10
|
+
* * stroke-width attr === '2.25'
|
|
11
|
+
* * data-topo-hub-spoke-stroke-width-active === '2.25'
|
|
12
|
+
* * data-topo-hub-spoke-opacity === '0.8' (R391 invariant)
|
|
13
|
+
* * stroke-linecap === 'round' (R382 invariant)
|
|
14
|
+
* - Idle spokes (no flow): stroke-width === '1' (R415 doesn't touch idle)
|
|
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
|
+
|
|
22
|
+
const browser = await chromium.launch({ headless: true });
|
|
23
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
27
|
+
});
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias) => ({
|
|
33
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
36
|
+
});
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
|
|
38
|
+
});
|
|
39
|
+
// Seed flow alpha→beta so alpha + beta spokes become active
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
41
|
+
json: { messages: [
|
|
42
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
43
|
+
] },
|
|
44
|
+
}));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForSelector('[data-topo-hub-spoke-active]', { timeout: 15000 });
|
|
50
|
+
await page.waitForTimeout(500);
|
|
51
|
+
|
|
52
|
+
const probe = await page.evaluate(() => {
|
|
53
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
54
|
+
const active = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'true');
|
|
55
|
+
const idle = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'false');
|
|
56
|
+
const read = (el) => el ? ({
|
|
57
|
+
strokeWidth: el.getAttribute('stroke-width'),
|
|
58
|
+
strokeWidthData: el.getAttribute('data-topo-hub-spoke-stroke-width-active'),
|
|
59
|
+
opacity: el.getAttribute('opacity'),
|
|
60
|
+
opacityData: el.getAttribute('data-topo-hub-spoke-opacity'),
|
|
61
|
+
linecap: el.getAttribute('stroke-linecap'),
|
|
62
|
+
}) : null;
|
|
63
|
+
return {
|
|
64
|
+
total: spokes.length,
|
|
65
|
+
activeCount: active.length,
|
|
66
|
+
idleCount: idle.length,
|
|
67
|
+
activeSample: read(active[0]),
|
|
68
|
+
idleSample: read(idle[0]),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
// 3 spokes total (alpha, beta, gamma)
|
|
76
|
+
total_3_spokes: probe.total === 3,
|
|
77
|
+
// alpha + beta active (flow link)
|
|
78
|
+
active_2_spokes: probe.activeCount === 2,
|
|
79
|
+
idle_1_spoke: probe.idleCount === 1,
|
|
80
|
+
// R415: active strokeWidth '2.25'
|
|
81
|
+
active_strokeWidth_2_25: probe.activeSample?.strokeWidth === '2.25',
|
|
82
|
+
active_data_2_25: probe.activeSample?.strokeWidthData === '2.25',
|
|
83
|
+
// R391 opacity invariant
|
|
84
|
+
active_opacity_0_8: probe.activeSample?.opacity === '0.8',
|
|
85
|
+
// R382 linecap invariant
|
|
86
|
+
active_linecap_round: probe.activeSample?.linecap === 'round',
|
|
87
|
+
// Idle invariant: strokeWidth '1' (R415 doesn't touch idle)
|
|
88
|
+
idle_strokeWidth_1: probe.idleSample?.strokeWidth === '1',
|
|
89
|
+
idle_opacity_0_45: probe.idleSample?.opacity === '0.45',
|
|
90
|
+
};
|
|
91
|
+
const ok = Object.values(results).every(Boolean);
|
|
92
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke active strokeWidth 2 → 2.25:`, JSON.stringify(results),
|
|
93
|
+
'\n active:', probe.activeSample,
|
|
94
|
+
'\n idle: ', probe.idleSample);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* Round 419 verification: hub-spoke idle opacity 0.45 → 0.50.
|
|
2
|
+
* Stale-state legibility lift family 9th anchor — idle spokes
|
|
3
|
+
* read more confidently while the active/idle contrast ratio
|
|
4
|
+
* stays clear (0.8/0.50 = 1.6×).
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - Seed a flow alpha→beta so 2 spokes become active, 1 idle
|
|
8
|
+
* - Idle spoke:
|
|
9
|
+
* * opacity attr === '0.5'
|
|
10
|
+
* * data-topo-hub-spoke-opacity === '0.5'
|
|
11
|
+
* * data-topo-hub-spoke-active === 'false'
|
|
12
|
+
* * R46 className 'anet-topo-spoke-flow' preserved
|
|
13
|
+
* - Active spoke (R391/R415 invariants):
|
|
14
|
+
* * opacity '0.8'
|
|
15
|
+
* * strokeWidth '2.25'
|
|
16
|
+
*/
|
|
17
|
+
import { chromium } from 'playwright';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
|
|
20
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
21
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
22
|
+
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 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
|
+
const mk = (alias) => ({
|
|
34
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
35
|
+
network_id: nid, project_dir: null,
|
|
36
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
37
|
+
});
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
41
|
+
json: { messages: [
|
|
42
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
43
|
+
] },
|
|
44
|
+
}));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForSelector('[data-topo-hub-spoke-active]', { timeout: 15000 });
|
|
50
|
+
await page.waitForTimeout(500);
|
|
51
|
+
|
|
52
|
+
const probe = await page.evaluate(() => {
|
|
53
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
54
|
+
const active = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'true');
|
|
55
|
+
const idle = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'false');
|
|
56
|
+
const read = (el) => el ? ({
|
|
57
|
+
opacity: el.getAttribute('opacity'),
|
|
58
|
+
opacityData: el.getAttribute('data-topo-hub-spoke-opacity'),
|
|
59
|
+
strokeWidth: el.getAttribute('stroke-width'),
|
|
60
|
+
className: el.getAttribute('class'),
|
|
61
|
+
}) : null;
|
|
62
|
+
return {
|
|
63
|
+
total: spokes.length,
|
|
64
|
+
activeCount: active.length,
|
|
65
|
+
idleCount: idle.length,
|
|
66
|
+
activeSample: read(active[0]),
|
|
67
|
+
idleSample: read(idle[0]),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const results = {
|
|
74
|
+
// Setup: 2 active + 1 idle
|
|
75
|
+
total_3_spokes: probe.total === 3,
|
|
76
|
+
active_2: probe.activeCount === 2,
|
|
77
|
+
idle_1: probe.idleCount === 1,
|
|
78
|
+
// R419: idle opacity '0.5'
|
|
79
|
+
idle_opacity_0_5: probe.idleSample?.opacity === '0.5',
|
|
80
|
+
idle_data_0_5: probe.idleSample?.opacityData === '0.5',
|
|
81
|
+
// R46 idle className preserved
|
|
82
|
+
idle_has_flow_class: probe.idleSample?.className?.includes('anet-topo-spoke-flow') === true,
|
|
83
|
+
// R391/R415 active invariants
|
|
84
|
+
active_opacity_0_8: probe.activeSample?.opacity === '0.8',
|
|
85
|
+
active_strokeWidth_2_25: probe.activeSample?.strokeWidth === '2.25',
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke idle opacity 0.45 → 0.50:`, JSON.stringify(results),
|
|
89
|
+
'\n active:', probe.activeSample,
|
|
90
|
+
'\n idle: ', probe.idleSample);
|
|
91
|
+
process.exit(ok ? 0 : 1);
|