@sleep2agi/agent-network-dashboard 0.5.1-preview.7 → 0.5.1-preview.71
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/04.j~y8e~sbs4.js +1 -0
- package/.next/static/chunks/06llamqb4jsu..js +4 -0
- package/.next/static/chunks/0_p8jkzdw5x2_.css +2 -0
- package/.next/static/chunks/12heglqfrp1bm.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +1426 -84
- 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-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-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-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-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-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-linecap-test.mjs +80 -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-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-halo-offline-opacity-test.mjs +87 -0
- package/scripts/topo-node-pulse-peak-test.mjs +89 -0
- package/scripts/topo-panel-count-letterspacing-test.mjs +89 -0
- package/scripts/topo-panel-rect-opacity-hover-test.mjs +109 -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-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-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-zoom-icon-hover-scale-test.mjs +114 -0
- package/scripts/topo-zoom-level-hover-letterspacing-test.mjs +91 -0
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/0bja1amnrg3li.js +0 -1
- package/.next/static/chunks/0k~uc0~~19hyy.js +0 -4
- package/.next/static/chunks/0wtq_6dnzems6.js +0 -1
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_buildManifest.js +0 -0
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 399 verification: active-links chip closes the 3-chip
|
|
2
|
+
* chip-row by extending R398 hover translate-y-px lift onto the
|
|
3
|
+
* third (rightmost) chip. Gated to clickable variant (flowLinks
|
|
4
|
+
* > 0) so empty active-links chip stays planted.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - With seeded messages (active flow links present):
|
|
8
|
+
* * data-chip-hover-lift === 'true'
|
|
9
|
+
* * data-active-links-clickable === 'true'
|
|
10
|
+
* * className includes 'hover:-translate-y-px'
|
|
11
|
+
* * className includes 'transition-transform' + 'transform-gpu'
|
|
12
|
+
* - Pre-R399 invariants preserved:
|
|
13
|
+
* * R232 tabular-nums + R313 font-medium + R266 chip-focus
|
|
14
|
+
* * R193 hover:bg-cyan-500/10 + hover:text-cyan-200 + hover:border-cyan-500/30
|
|
15
|
+
* * inline transition list still covers color/bg/border/opacity
|
|
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, status) => ({
|
|
34
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
35
|
+
network_id: nid, project_dir: null,
|
|
36
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
37
|
+
});
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
39
|
+
mk('alpha', 'working'),
|
|
40
|
+
mk('beta', 'idle'),
|
|
41
|
+
mk('gamma', 'idle'),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
// Seed messages so flowLinks > 0 → chip becomes clickable
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
46
|
+
json: { 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: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
|
|
49
|
+
] },
|
|
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-active-links-chip]', { timeout: 15000 });
|
|
56
|
+
await page.waitForTimeout(600);
|
|
57
|
+
|
|
58
|
+
const probe = await page.evaluate(() => {
|
|
59
|
+
const chip = document.querySelector('[data-active-links-chip]');
|
|
60
|
+
return chip ? {
|
|
61
|
+
className: chip.className,
|
|
62
|
+
lift: chip.getAttribute('data-chip-hover-lift'),
|
|
63
|
+
clickable: chip.getAttribute('data-active-links-clickable'),
|
|
64
|
+
flowCount: chip.getAttribute('data-active-links-flow-count'),
|
|
65
|
+
} : null;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const cls = probe?.className || '';
|
|
71
|
+
const results = {
|
|
72
|
+
// Setup: active-links chip mounted with flow > 0
|
|
73
|
+
chip_mounted: !!probe,
|
|
74
|
+
flow_count_present: parseInt(probe?.flowCount || '0', 10) > 0,
|
|
75
|
+
// R399 lift attrs + className tokens (clickable variant)
|
|
76
|
+
lift_true: probe?.lift === 'true',
|
|
77
|
+
clickable_true: probe?.clickable === 'true',
|
|
78
|
+
has_hover_translate: cls.includes('hover:-translate-y-px'),
|
|
79
|
+
has_transition_transform: cls.includes('transition-transform'),
|
|
80
|
+
has_transform_gpu: cls.includes('transform-gpu'),
|
|
81
|
+
// Pre-R399 invariants
|
|
82
|
+
has_tabular_nums: cls.includes('tabular-nums'),
|
|
83
|
+
has_font_medium: cls.includes('font-medium'),
|
|
84
|
+
has_chip_focus: cls.includes('anet-topo-chip-focus'),
|
|
85
|
+
has_hover_cyan_bg: cls.includes('hover:bg-cyan-500/10'), // R193 invariant
|
|
86
|
+
has_hover_cyan_text: cls.includes('hover:text-cyan-200'),
|
|
87
|
+
has_hover_cyan_border: cls.includes('hover:border-cyan-500/30'),
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} active-links chip hover lift translateY(-1px):`, JSON.stringify(results),
|
|
91
|
+
'\n className:', cls,
|
|
92
|
+
'\n flowCount:', probe?.flowCount, '/ lift:', probe?.lift, '/ clickable:', probe?.clickable);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 362 verification: 3 chip-row chip digits (working / online /
|
|
2
|
+
* active-links) wrap their digit in a font-semibold span, lifting it
|
|
3
|
+
* fw 500 (chip-row R313 baseline) → fw 600 within each chip. The
|
|
4
|
+
* outer chip className keeps font-medium so the chip's overall data-
|
|
5
|
+
* weight stays consistent with the R313 family; the digit override
|
|
6
|
+
* adds a within-chip tier:
|
|
7
|
+
* digit fw 600 + full opacity
|
|
8
|
+
* unit fw 500 + R338 opacity-70
|
|
9
|
+
*
|
|
10
|
+
* Joins the R333-R341 chip-internal-hierarchy arc at the chip-count
|
|
11
|
+
* scope (8th surface family).
|
|
12
|
+
*
|
|
13
|
+
* Contract (cyber, fresh fixture):
|
|
14
|
+
* - Each of the 3 chip digit spans has data-{name}-chip-digit attr.
|
|
15
|
+
* - Computed fontWeight of the digit span === '600'.
|
|
16
|
+
* - The unit sibling span still has opacity 0.7 + data-{name}-chip-
|
|
17
|
+
* unit attr (R338 invariants).
|
|
18
|
+
* - Parent chip retains font-medium (R313 baseline).
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
41
|
+
});
|
|
42
|
+
const now = Date.now();
|
|
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
|
+
] } }));
|
|
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
|
+
await page.waitForSelector('[data-working-chip-digit]', { timeout: 15000 });
|
|
52
|
+
await page.waitForSelector('[data-online-chip-digit]', { timeout: 5000 });
|
|
53
|
+
await page.waitForSelector('[data-active-links-chip-digit]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
function readChip(digitSel, unitSel, parentSel) {
|
|
58
|
+
const digit = document.querySelector(digitSel);
|
|
59
|
+
const unit = document.querySelector(unitSel);
|
|
60
|
+
const parent = document.querySelector(parentSel);
|
|
61
|
+
return {
|
|
62
|
+
digitFw: digit ? getComputedStyle(digit).fontWeight : null,
|
|
63
|
+
digitText: digit?.textContent ?? null,
|
|
64
|
+
unitOp: unit ? getComputedStyle(unit).opacity : null,
|
|
65
|
+
unitPresent: !!unit,
|
|
66
|
+
parentCls: parent?.getAttribute('class') ?? null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
working: readChip('[data-working-chip-digit]', '[data-working-chip-unit]', '[data-working-chip]'),
|
|
71
|
+
online: readChip('[data-online-chip-digit]', '[data-online-chip-unit]', '[data-online-chip]'),
|
|
72
|
+
activeLinks: readChip('[data-active-links-chip-digit]', '[data-active-links-chip-unit]', '[data-active-links-chip]'),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await browser.close();
|
|
77
|
+
|
|
78
|
+
const isFw600 = (v) => v === '600' || v === 'bold'; // Tailwind font-semibold === 600
|
|
79
|
+
const approxEq = (a, b, tol = 0.02) => {
|
|
80
|
+
const x = parseFloat(a); const y = parseFloat(b);
|
|
81
|
+
return Number.isFinite(x) && Number.isFinite(y) && Math.abs(x - y) <= tol;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const checkChip = (chip) => ({
|
|
85
|
+
digit_fw_600: isFw600(chip.digitFw),
|
|
86
|
+
unit_op_0_7: approxEq(chip.unitOp, 0.7), // R338
|
|
87
|
+
unit_present: chip.unitPresent,
|
|
88
|
+
parent_medium: /font-medium/.test(chip.parentCls || ''), // R313
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
working_digit_fw_600: checkChip(probe.working).digit_fw_600,
|
|
93
|
+
working_unit_op_0_7: checkChip(probe.working).unit_op_0_7,
|
|
94
|
+
working_parent_medium: checkChip(probe.working).parent_medium,
|
|
95
|
+
online_digit_fw_600: checkChip(probe.online).digit_fw_600,
|
|
96
|
+
online_unit_op_0_7: checkChip(probe.online).unit_op_0_7,
|
|
97
|
+
online_parent_medium: checkChip(probe.online).parent_medium,
|
|
98
|
+
active_digit_fw_600: checkChip(probe.activeLinks).digit_fw_600,
|
|
99
|
+
active_unit_op_0_7: checkChip(probe.activeLinks).unit_op_0_7,
|
|
100
|
+
active_parent_medium: checkChip(probe.activeLinks).parent_medium,
|
|
101
|
+
};
|
|
102
|
+
const ok = Object.values(results).every(Boolean);
|
|
103
|
+
console.log(`${ok ? '✅' : '❌'} chip-row digit fontWeight 500 → 600:`, JSON.stringify(results),
|
|
104
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
105
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 398 verification: chip-row chips (working / online) gain
|
|
2
|
+
* hover translateY(-1px) lift on clickable variant only. Extends
|
|
3
|
+
* R397's filter-pill lift to the static header chips, gated to
|
|
4
|
+
* the chip's clickable role so empty chips stay planted.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - Populated working chip (workingCount > 0):
|
|
8
|
+
* * data-chip-hover-lift === 'true'
|
|
9
|
+
* * className includes 'hover:-translate-y-px'
|
|
10
|
+
* * className includes 'transition-transform' + 'transform-gpu'
|
|
11
|
+
* - Populated online chip (onlineNodes.length > 0):
|
|
12
|
+
* * data-chip-hover-lift === 'true'
|
|
13
|
+
* * className includes 'hover:-translate-y-px'
|
|
14
|
+
* - Pre-R398 invariants preserved:
|
|
15
|
+
* * transition-colors duration-200 still present
|
|
16
|
+
* * R201 hover:bg + hover:border tokens still present
|
|
17
|
+
* * anet-topo-chip-focus + tabular-nums + font-medium present
|
|
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, status) => ({
|
|
36
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('alpha', 'working'),
|
|
42
|
+
mk('beta', 'idle'),
|
|
43
|
+
mk('gamma', 'idle'),
|
|
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
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 15000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const probe = await page.evaluate(() => {
|
|
55
|
+
const work = document.querySelector('[data-working-chip]');
|
|
56
|
+
const online = document.querySelector('[data-online-chip]');
|
|
57
|
+
return {
|
|
58
|
+
work: work ? {
|
|
59
|
+
className: work.className,
|
|
60
|
+
lift: work.getAttribute('data-chip-hover-lift'),
|
|
61
|
+
clickable: work.getAttribute('data-working-chip-clickable'),
|
|
62
|
+
} : null,
|
|
63
|
+
online: online ? {
|
|
64
|
+
className: online.className,
|
|
65
|
+
lift: online.getAttribute('data-chip-hover-lift'),
|
|
66
|
+
clickable: online.getAttribute('data-online-chip-clickable'),
|
|
67
|
+
} : null,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const wc = probe.work?.className || '';
|
|
74
|
+
const oc = probe.online?.className || '';
|
|
75
|
+
const results = {
|
|
76
|
+
// working chip: clickable + R398 lift attrs + className tokens
|
|
77
|
+
work_lift_true: probe.work?.lift === 'true',
|
|
78
|
+
work_clickable_true: probe.work?.clickable === 'true',
|
|
79
|
+
work_has_hover_translate: wc.includes('hover:-translate-y-px'),
|
|
80
|
+
work_has_transition_transform: wc.includes('transition-transform'),
|
|
81
|
+
work_has_transform_gpu: wc.includes('transform-gpu'),
|
|
82
|
+
work_has_transition_colors: wc.includes('transition-colors'), // R201 preserved
|
|
83
|
+
work_has_hover_bg: wc.includes('hover:bg-green-500/15'), // R201 preserved
|
|
84
|
+
work_has_chip_focus: wc.includes('anet-topo-chip-focus'),
|
|
85
|
+
// online chip mirror
|
|
86
|
+
online_lift_true: probe.online?.lift === 'true',
|
|
87
|
+
online_clickable_true: probe.online?.clickable === 'true',
|
|
88
|
+
online_has_hover_translate: oc.includes('hover:-translate-y-px'),
|
|
89
|
+
online_has_transition_transform: oc.includes('transition-transform'),
|
|
90
|
+
};
|
|
91
|
+
const ok = Object.values(results).every(Boolean);
|
|
92
|
+
console.log(`${ok ? '✅' : '❌'} chip-row chips hover lift translateY(-1px):`, JSON.stringify(results),
|
|
93
|
+
'\n workingClass:', wc,
|
|
94
|
+
'\n onlineClass: ', oc);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Round 400 verification (milestone): chrome reset + fullscreen
|
|
2
|
+
* buttons gain hover translateY(-1px) lift. Closes the hover-lift
|
|
3
|
+
* gesture across every standalone interactive HTML element in
|
|
4
|
+
* TopoGraph. Segmented controls (zoom/-+, nodeSize S/M/L,
|
|
5
|
+
* Layout Ring/Grid) intentionally stay planted to preserve the
|
|
6
|
+
* unified-strip appearance.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - Reset button:
|
|
10
|
+
* * data-topo-chrome-reset-hover-lift === 'true'
|
|
11
|
+
* * className includes 'hover:-translate-y-px'
|
|
12
|
+
* * className includes 'transition-transform' + 'transform-gpu'
|
|
13
|
+
* - Fullscreen button:
|
|
14
|
+
* * data-topo-chrome-fullscreen-hover-lift === 'true'
|
|
15
|
+
* * className includes 'hover:-translate-y-px'
|
|
16
|
+
* * className includes 'transition-transform' + 'transform-gpu'
|
|
17
|
+
* - Pre-R400 invariants preserved:
|
|
18
|
+
* * R196 active:bg deeper press states
|
|
19
|
+
* * R288 strokeWidth=2.5 on chrome icons
|
|
20
|
+
* * R350 hover-rotate (reset) / R353 group-hover scale (fullscreen)
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const mk = (alias) => ({
|
|
39
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
40
|
+
network_id: nid, project_dir: null,
|
|
41
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
42
|
+
});
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta') ] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForSelector('[data-topo-chrome-reset]', { timeout: 15000 });
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
const probe = await page.evaluate(() => {
|
|
54
|
+
const reset = document.querySelector('[data-topo-chrome-reset]');
|
|
55
|
+
const fs = document.querySelector('[data-topo-chrome-fullscreen]');
|
|
56
|
+
return {
|
|
57
|
+
reset: reset ? {
|
|
58
|
+
className: reset.className,
|
|
59
|
+
lift: reset.getAttribute('data-topo-chrome-reset-hover-lift'),
|
|
60
|
+
} : null,
|
|
61
|
+
fs: fs ? {
|
|
62
|
+
className: fs.className,
|
|
63
|
+
lift: fs.getAttribute('data-topo-chrome-fullscreen-hover-lift'),
|
|
64
|
+
} : null,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const rc = probe.reset?.className || '';
|
|
71
|
+
const fc = probe.fs?.className || '';
|
|
72
|
+
const results = {
|
|
73
|
+
// Reset button R400 wiring
|
|
74
|
+
reset_lift_true: probe.reset?.lift === 'true',
|
|
75
|
+
reset_has_hover_translate: rc.includes('hover:-translate-y-px'),
|
|
76
|
+
reset_has_transition_transform: rc.includes('transition-transform'),
|
|
77
|
+
reset_has_transform_gpu: rc.includes('transform-gpu'),
|
|
78
|
+
reset_has_transition_colors: rc.includes('transition-colors'), // pre-R400 preserved
|
|
79
|
+
reset_has_active_bg: rc.includes('active:bg-white/10'), // R196 preserved
|
|
80
|
+
reset_has_rounded_md: rc.includes('rounded-md'),
|
|
81
|
+
// Fullscreen button R400 wiring
|
|
82
|
+
fs_lift_true: probe.fs?.lift === 'true',
|
|
83
|
+
fs_has_hover_translate: fc.includes('hover:-translate-y-px'),
|
|
84
|
+
fs_has_transition_transform: fc.includes('transition-transform'),
|
|
85
|
+
fs_has_transform_gpu: fc.includes('transform-gpu'),
|
|
86
|
+
fs_has_transition_colors: fc.includes('transition-colors'),
|
|
87
|
+
fs_has_group: fc.includes('group'), // R353 invariant
|
|
88
|
+
fs_has_rounded_md: fc.includes('rounded-md'),
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} chrome reset + fullscreen hover lift translateY(-1px):`, JSON.stringify(results),
|
|
92
|
+
'\n resetClass:', rc,
|
|
93
|
+
'\n fsClass: ', fc);
|
|
94
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Round 376 verification: nodeSize + zoom segmented control wrappers
|
|
2
|
+
* rounded-md → rounded-lg (6 → 8 px). Sibling polish to R375 Layout-
|
|
3
|
+
* toggle wrapper — three chrome-strip segmented controls now all
|
|
4
|
+
* share rounded-lg at the wrapper tier:
|
|
5
|
+
* R375 Layout-toggle wrapper rounded-lg 8 px
|
|
6
|
+
* R376 nodeSize wrapper rounded-lg 8 px (this round)
|
|
7
|
+
* R376 zoom wrapper rounded-lg 8 px (this round)
|
|
8
|
+
*
|
|
9
|
+
* Individual atomic chrome buttons (reset, fullscreen) keep rounded-
|
|
10
|
+
* md (6 px) as their own atomic-button tier — chrome strip now
|
|
11
|
+
* expresses a two-tier hierarchy: 'segmented control container'
|
|
12
|
+
* (rounded-lg) vs 'standalone button' (rounded-md).
|
|
13
|
+
*
|
|
14
|
+
* Contract:
|
|
15
|
+
* - [data-topo-chrome-nodesize-radius] computed border-radius '8px'.
|
|
16
|
+
* - data-topo-chrome-nodesize-radius === 'rounded-lg'.
|
|
17
|
+
* - [data-topo-chrome-zoom-wrapper-radius] computed border-radius '8px'.
|
|
18
|
+
* - data-topo-chrome-zoom-wrapper-radius === 'rounded-lg'.
|
|
19
|
+
* - Pre-R376 invariants: border 1px + flex display + overflow:hidden
|
|
20
|
+
* + R263 background-color/border-color transition preserved on
|
|
21
|
+
* each wrapper.
|
|
22
|
+
* - Atomic reset + fullscreen buttons still have border-radius 6px
|
|
23
|
+
* (rounded-md, untouched).
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias) => ({
|
|
41
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForSelector('[data-topo-chrome-nodesize-radius]', { timeout: 15000 });
|
|
53
|
+
await page.waitForSelector('[data-topo-chrome-zoom-wrapper-radius]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const nodesize = document.querySelector('[data-topo-chrome-nodesize-radius]');
|
|
58
|
+
const zoom = document.querySelector('[data-topo-chrome-zoom-wrapper-radius]');
|
|
59
|
+
const reset = document.querySelector('[data-topo-chrome-reset]');
|
|
60
|
+
const fs = document.querySelector('[data-topo-chrome-fullscreen]');
|
|
61
|
+
const ncs = nodesize ? getComputedStyle(nodesize) : null;
|
|
62
|
+
const zcs = zoom ? getComputedStyle(zoom) : null;
|
|
63
|
+
const rcs = reset ? getComputedStyle(reset) : null;
|
|
64
|
+
const fcs = fs ? getComputedStyle(fs) : null;
|
|
65
|
+
return {
|
|
66
|
+
nodesizeRadius: ncs?.borderRadius ?? null,
|
|
67
|
+
nodesizeData: nodesize?.getAttribute('data-topo-chrome-nodesize-radius') ?? null,
|
|
68
|
+
nodesizeBorderW: ncs?.borderWidth ?? null,
|
|
69
|
+
nodesizeTrans: ncs?.transition ?? null,
|
|
70
|
+
zoomRadius: zcs?.borderRadius ?? null,
|
|
71
|
+
zoomData: zoom?.getAttribute('data-topo-chrome-zoom-wrapper-radius') ?? null,
|
|
72
|
+
zoomBorderW: zcs?.borderWidth ?? null,
|
|
73
|
+
zoomTrans: zcs?.transition ?? null,
|
|
74
|
+
resetRadius: rcs?.borderRadius ?? null,
|
|
75
|
+
fullscreenRadius: fcs?.borderRadius ?? null,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await browser.close();
|
|
80
|
+
|
|
81
|
+
const hasTrans = (s, prop) =>
|
|
82
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
nodesize_radius_8px: probe.nodesizeRadius === '8px', // rounded-lg
|
|
86
|
+
nodesize_data_lg: probe.nodesizeData === 'rounded-lg',
|
|
87
|
+
nodesize_border_1px: probe.nodesizeBorderW === '1px',
|
|
88
|
+
nodesize_trans_border: hasTrans(probe.nodesizeTrans, 'border-color'), // R263 / R268
|
|
89
|
+
zoom_radius_8px: probe.zoomRadius === '8px',
|
|
90
|
+
zoom_data_lg: probe.zoomData === 'rounded-lg',
|
|
91
|
+
zoom_border_1px: probe.zoomBorderW === '1px',
|
|
92
|
+
zoom_trans_border: hasTrans(probe.zoomTrans, 'border-color'),
|
|
93
|
+
// Atomic chrome buttons stay rounded-md (6 px) — invariant.
|
|
94
|
+
reset_still_md: probe.resetRadius === '6px',
|
|
95
|
+
fullscreen_still_md: probe.fullscreenRadius === '6px',
|
|
96
|
+
};
|
|
97
|
+
const ok = Object.values(results).every(Boolean);
|
|
98
|
+
console.log(`${ok ? '✅' : '❌'} chrome segmented wrappers rounded-md → rounded-lg:`, JSON.stringify(results),
|
|
99
|
+
'\n probe:', probe);
|
|
100
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 403 verification: click-ripple SMIL initial opacity 0.7 →
|
|
2
|
+
* 0.8. Theme-consistency / canvas-presence polish family (8th
|
|
3
|
+
* anchor) — click-feedback now starts at the same 0.8 alpha as
|
|
4
|
+
* R370 hub hover-ring and R391 hub-spoke active. Three canvas
|
|
5
|
+
* state-feedback indicators share a uniform 0.8 start alpha.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Click the hub to fire the ripple (R52 fitView click path)
|
|
9
|
+
* - data-click-ripple-start-opacity === '0.8' on the opacity
|
|
10
|
+
* <animate> element
|
|
11
|
+
* - The opacity animation `values` starts with '0.8'
|
|
12
|
+
* - Pre-R403 invariants preserved:
|
|
13
|
+
* * dur='0.5s'
|
|
14
|
+
* * R227 calcMode='spline'
|
|
15
|
+
* * R227 keySplines='0.25 0.1 0.25 1'
|
|
16
|
+
* * fill='freeze'
|
|
17
|
+
*
|
|
18
|
+
* Source-file verification fallback: the SMIL element appears
|
|
19
|
+
* only during the 500ms ripple window, so the test reads source
|
|
20
|
+
* if the runtime probe doesn't catch the element in flight.
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const mk = (alias) => ({
|
|
39
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
40
|
+
network_id: nid, project_dir: null,
|
|
41
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
42
|
+
});
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta') ] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForSelector('[data-topo-hub]', { timeout: 15000 });
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
// Click hub to fire ripple
|
|
54
|
+
await page.click('[data-topo-hub]');
|
|
55
|
+
await page.waitForTimeout(100); // ripple lives 500ms, probe early
|
|
56
|
+
|
|
57
|
+
const probe = await page.evaluate(() => {
|
|
58
|
+
const ripple = document.querySelector('[data-click-ripple]');
|
|
59
|
+
if (!ripple) return null;
|
|
60
|
+
const opacityAnim = ripple.querySelector('[data-click-ripple-start-opacity]');
|
|
61
|
+
return {
|
|
62
|
+
rippleMounted: true,
|
|
63
|
+
strokeWidth: ripple.getAttribute('stroke-width'),
|
|
64
|
+
fillAttr: ripple.getAttribute('fill'),
|
|
65
|
+
startOpacity: opacityAnim?.getAttribute('data-click-ripple-start-opacity') ?? null,
|
|
66
|
+
valuesAttr: opacityAnim?.getAttribute('values') ?? null,
|
|
67
|
+
durAttr: opacityAnim?.getAttribute('dur') ?? null,
|
|
68
|
+
calcMode: opacityAnim?.getAttribute('calcMode') ?? null,
|
|
69
|
+
keySplines: opacityAnim?.getAttribute('keySplines') ?? null,
|
|
70
|
+
fillFreeze: opacityAnim?.getAttribute('fill') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Source-file fallback in case the SMIL element is not caught
|
|
75
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
76
|
+
const sourceHasStartOpacity = fileText.includes('data-click-ripple-start-opacity="0.8"');
|
|
77
|
+
const sourceHasValues08 = fileText.includes('values="0.8;0"');
|
|
78
|
+
|
|
79
|
+
await browser.close();
|
|
80
|
+
|
|
81
|
+
const results = {
|
|
82
|
+
// Source-file invariants (deterministic from disk)
|
|
83
|
+
source_start_opacity_attr: sourceHasStartOpacity,
|
|
84
|
+
source_values_0_8_to_0: sourceHasValues08,
|
|
85
|
+
// Runtime probe (best-effort during 500ms ripple window)
|
|
86
|
+
ripple_mounted: !!probe,
|
|
87
|
+
start_opacity_0_8: probe?.startOpacity === '0.8',
|
|
88
|
+
values_0_8_to_0: probe?.valuesAttr === '0.8;0',
|
|
89
|
+
dur_0_5s: probe?.durAttr === '0.5s',
|
|
90
|
+
calcMode_spline: probe?.calcMode === 'spline',
|
|
91
|
+
keySplines_easeout: probe?.keySplines === '0.25 0.1 0.25 1',
|
|
92
|
+
// Static ring invariants
|
|
93
|
+
strokeWidth_2: probe?.strokeWidth === '2',
|
|
94
|
+
fill_none: probe?.fillAttr === 'none',
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} click-ripple SMIL opacity 0.7 → 0.8:`, JSON.stringify(results),
|
|
98
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
99
|
+
process.exit(ok ? 0 : 1);
|