@sleep2agi/agent-network-dashboard 0.5.3-preview.8 → 0.5.3-preview.80
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 +32 -32
- 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 +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -4
- 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/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.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/09opq.s35allz.js +1 -0
- package/.next/static/chunks/0mnksqen3wvwj.js +4 -0
- package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
- package/.next/static/chunks/0vl-xelss~c7q.css +2 -0
- package/.next/static/chunks/0ye8xjzn045zw.js +1 -0
- package/.next/static/chunks/{0200cxbmk961-.js → 13x8dy_zr3iit.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ServersDrawer.tsx +16 -3
- package/app/components/TopoGraph.tsx +2242 -116
- package/app/globals.css +81 -7
- package/package.json +4 -4
- package/scripts/p157-servers-copy-test.mjs +95 -0
- package/scripts/topo-active-chrome-hover-text-test.mjs +107 -0
- package/scripts/topo-alias-glow-test.mjs +121 -0
- package/scripts/topo-avatar-brightness-test.mjs +116 -0
- package/scripts/topo-avatar-fallback-hover-test.mjs +104 -0
- package/scripts/topo-brand-logo-breath-test.mjs +102 -0
- package/scripts/topo-brand-logo-hover-brightness-test.mjs +105 -0
- package/scripts/topo-brand-logo-hover-rotate-test.mjs +93 -0
- package/scripts/topo-brand-logo-hover-test.mjs +85 -0
- package/scripts/topo-chip-row-digit-ls-test.mjs +135 -0
- package/scripts/topo-chip-row-member-alias-lit-test.mjs +154 -0
- package/scripts/topo-chip-row-unit-hover-tracking-test.mjs +124 -0
- package/scripts/topo-cluster-count-attr-test.mjs +80 -0
- package/scripts/topo-crescent-breath-test.mjs +104 -0
- package/scripts/topo-crescent-recede-test.mjs +111 -0
- package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
- package/scripts/topo-edge-pill-glow-test.mjs +67 -0
- package/scripts/topo-filter-pill-glow-test.mjs +90 -0
- package/scripts/topo-fleet-density-tier-test.mjs +84 -0
- package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
- package/scripts/topo-fullscreen-attr-test.mjs +73 -0
- package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
- package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
- package/scripts/topo-group-label-member-alias-hover-test.mjs +125 -0
- package/scripts/topo-group-pill-glow-test.mjs +76 -0
- package/scripts/topo-hub-digit-ls-test.mjs +119 -0
- package/scripts/topo-hub-halo-glow-test.mjs +96 -0
- package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
- package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
- package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
- package/scripts/topo-hub-highlight-r-test.mjs +112 -0
- package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
- package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
- package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
- package/scripts/topo-hub-idle-breath-test.mjs +109 -0
- package/scripts/topo-hub-recede-test.mjs +124 -0
- package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
- package/scripts/topo-layout-hover-fw-test.mjs +98 -0
- package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
- package/scripts/topo-legend-label-fw-test.mjs +107 -0
- package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
- package/scripts/topo-legend-swatch-member-alias-match-test.mjs +139 -0
- package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
- package/scripts/topo-node-alias-brightness-test.mjs +84 -0
- package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
- package/scripts/topo-orphan-box-dash-test.mjs +89 -0
- package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
- package/scripts/topo-orphan-label-italic-test.mjs +90 -0
- package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
- package/scripts/topo-panel-count-hover-ls-test.mjs +87 -0
- package/scripts/topo-panel-title-glow-test.mjs +111 -0
- package/scripts/topo-pill-x-rotate-test.mjs +96 -0
- package/scripts/topo-pinned-aspect-test.mjs +89 -0
- package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
- package/scripts/topo-pressure-seg-member-alias-match-test.mjs +133 -0
- package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
- package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
- package/scripts/topo-recent-more-fw-test.mjs +126 -0
- package/scripts/topo-recent-row-fw-test.mjs +115 -0
- package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
- package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
- package/scripts/topo-runtime-badge-glow-test.mjs +108 -0
- package/scripts/topo-starfield-hue-test.mjs +109 -0
- package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
- package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
- package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
- package/scripts/topo-vendor-activelinks-press-test.mjs +100 -0
- package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
- package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
- package/scripts/topo-watermark-breath-test.mjs +100 -0
- package/scripts/topo-watermark-recede-test.mjs +114 -0
- package/scripts/topo-zoom-level-color-test.mjs +105 -0
- package/.next/static/chunks/07fkn2-8-6ejj.js +0 -4
- package/.next/static/chunks/0hndl9yzpqajt.css +0 -2
- package/.next/static/chunks/0nvcaq5be21x_.js +0 -1
- package/.next/static/chunks/0~r4zc7qlx96l.js +0 -1
- /package/.next/static/{iEkfox1TCVhnJmt_k5SoD → mwWrJ4INww4i53E7v_xMz}/_buildManifest.js +0 -0
- /package/.next/static/{iEkfox1TCVhnJmt_k5SoD → mwWrJ4INww4i53E7v_xMz}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{iEkfox1TCVhnJmt_k5SoD → mwWrJ4INww4i53E7v_xMz}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 499 verification: orphan band "其他" label gets fontStyle:
|
|
2
|
+
* italic, while prefix-group labels (alpha/beta/etc.) stay roman.
|
|
3
|
+
* Pure typography differentiation — no opacity / weight / fill / bbox
|
|
4
|
+
* change. data-group-label-orphan attr surfaces the gate for tests.
|
|
5
|
+
*
|
|
6
|
+
* Fixture: 2 prefix groups (alpha×3, beta×2) + 3 orphans (zeta/omega/
|
|
7
|
+
* lonely) — forces #150 orphan-band creation with at least 1 orphan
|
|
8
|
+
* and ≥ 1 prefix-group for differential assertion.
|
|
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: 1200 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try {
|
|
21
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
22
|
+
// grid layout required for groupBoxes to render
|
|
23
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
24
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
} catch {}
|
|
26
|
+
});
|
|
27
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
28
|
+
const r = await route.fetch();
|
|
29
|
+
const b = await r.json();
|
|
30
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
31
|
+
const mk = (alias, status) => ({
|
|
32
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
37
|
+
mk('alpha·1', 'working'), mk('alpha·2', 'idle'), mk('alpha·3', 'idle'),
|
|
38
|
+
mk('beta·1', 'working'), mk('beta·2', 'idle'),
|
|
39
|
+
mk('zeta', 'idle'),
|
|
40
|
+
mk('omega', 'idle'),
|
|
41
|
+
mk('lonely', 'idle'),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
48
|
+
await page.waitForSelector('[data-group-label]', { timeout: 15000 });
|
|
49
|
+
await page.waitForTimeout(1500);
|
|
50
|
+
|
|
51
|
+
const labels = await page.evaluate(() => {
|
|
52
|
+
const els = Array.from(document.querySelectorAll('[data-group-label]'));
|
|
53
|
+
return els.map((el) => {
|
|
54
|
+
const cs = window.getComputedStyle(el);
|
|
55
|
+
return {
|
|
56
|
+
key: el.getAttribute('data-group-label'),
|
|
57
|
+
orphan_attr:el.getAttribute('data-group-label-orphan'),
|
|
58
|
+
font_style: cs.fontStyle,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await browser.close();
|
|
64
|
+
|
|
65
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
66
|
+
const sourceTypeWired = /groupBoxes: \[\] as \{ key: string; isOrphan\?: boolean;/.test(src);
|
|
67
|
+
const sourceFieldWired = /isOrphan: !!band\.isOrphan,/.test(src);
|
|
68
|
+
const sourceStyleWired = /fontStyle: box\.isOrphan \? 'italic' : undefined/.test(src);
|
|
69
|
+
const sourceAttrWired = /data-group-label-orphan=\{box\.isOrphan \? 'true' : 'false'\}/.test(src);
|
|
70
|
+
|
|
71
|
+
const orphanLabel = labels.find((l) => l.key === '其他');
|
|
72
|
+
const prefixLabels = labels.filter((l) => l.key !== '其他' && l.key !== '');
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
found_labels: labels.length >= 2,
|
|
76
|
+
orphan_label_present: !!orphanLabel,
|
|
77
|
+
orphan_attr_true: orphanLabel && orphanLabel.orphan_attr === 'true',
|
|
78
|
+
orphan_font_italic: orphanLabel && orphanLabel.font_style === 'italic',
|
|
79
|
+
prefix_labels_present: prefixLabels.length >= 1,
|
|
80
|
+
prefix_attrs_false: prefixLabels.length > 0 && prefixLabels.every((p) => p.orphan_attr === 'false'),
|
|
81
|
+
prefix_font_normal: prefixLabels.length > 0 && prefixLabels.every((p) => p.font_style !== 'italic'),
|
|
82
|
+
source_type_wired: sourceTypeWired,
|
|
83
|
+
source_field_wired: sourceFieldWired,
|
|
84
|
+
source_style_wired: sourceStyleWired,
|
|
85
|
+
source_attr_wired: sourceAttrWired,
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} R499 orphan label italic:`, JSON.stringify(results),
|
|
89
|
+
'\n labels:', JSON.stringify(labels));
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* Round 551 verification: orphan band ("其他") rest-state LABEL opacity
|
|
2
|
+
* drops 0.55 → 0.4. 4th channel in the category-differentiation family
|
|
3
|
+
* (R499 italic, R503 dash, R506 rect fill, R551 label paint).
|
|
4
|
+
*
|
|
5
|
+
* Needs an orphan group to exist. The cluster-by-prefix logic creates
|
|
6
|
+
* an orphan band whenever there are sessions without a recognizable
|
|
7
|
+
* prefix (alias with no separator). Mock with a mix: 6 sessions with
|
|
8
|
+
* "alpha-" prefix (groups together) + 2 sessions with no separator
|
|
9
|
+
* (orphan).
|
|
10
|
+
*
|
|
11
|
+
* Test phases:
|
|
12
|
+
* 1. confirm grid layout active
|
|
13
|
+
* 2. locate orphan group-label vs prefix group-label
|
|
14
|
+
* 3. orphan rest opacity = 0.4, opacity attr surfaces value
|
|
15
|
+
* 4. prefix-group rest opacity = 0.55 (unchanged)
|
|
16
|
+
* 5. source-side regex confirms wiring
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
23
|
+
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try {
|
|
29
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
30
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
31
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
32
|
+
} catch {}
|
|
33
|
+
});
|
|
34
|
+
// Mock: 6 alpha-prefix sessions (prefix group) + 3 standalone (orphan band).
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias, status = 'idle') => ({
|
|
40
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha·1'), mk('alpha·2'), mk('alpha·3'),
|
|
46
|
+
mk('alpha·4'), mk('alpha·5'), mk('alpha·6'),
|
|
47
|
+
mk('foo'), mk('bar'), mk('baz'),
|
|
48
|
+
] } });
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
const page = await ctx.newPage();
|
|
53
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
54
|
+
await page.waitForSelector('[data-group-label]', { timeout: 15000 });
|
|
55
|
+
await page.waitForTimeout(500);
|
|
56
|
+
|
|
57
|
+
const groups = await page.evaluate(() => {
|
|
58
|
+
const els = Array.from(document.querySelectorAll('[data-group-label]'));
|
|
59
|
+
return els.map(el => ({
|
|
60
|
+
key: el.getAttribute('data-group-label'),
|
|
61
|
+
orphan: el.getAttribute('data-group-label-orphan') === 'true',
|
|
62
|
+
opacityAttr: el.getAttribute('data-group-label-opacity'),
|
|
63
|
+
computedOpacity: getComputedStyle(el).opacity,
|
|
64
|
+
pinned: el.getAttribute('data-group-label-pinned') === 'true',
|
|
65
|
+
hovered: el.getAttribute('data-group-label-hovered') === 'true',
|
|
66
|
+
}));
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
const orphan = groups.find(g => g.orphan);
|
|
72
|
+
const prefix = groups.find(g => !g.orphan);
|
|
73
|
+
|
|
74
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
75
|
+
const sourceWired =
|
|
76
|
+
/opacity=\{isPinned \|\| isHovered \? 1 : box\.isOrphan \? 0\.4 : 0\.55\}/.test(src);
|
|
77
|
+
const sourceAttrWired =
|
|
78
|
+
/data-group-label-opacity=\{isPinned \|\| isHovered \? 1 : box\.isOrphan \? 0\.4 : 0\.55\}/.test(src);
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
orphan_present: !!orphan,
|
|
82
|
+
prefix_present: !!prefix,
|
|
83
|
+
orphan_attr_0_4: orphan?.opacityAttr === '0.4',
|
|
84
|
+
orphan_computed_0_4: orphan ? Math.abs(parseFloat(orphan.computedOpacity) - 0.4) < 0.001 : false,
|
|
85
|
+
prefix_attr_0_55: prefix?.opacityAttr === '0.55',
|
|
86
|
+
prefix_computed_0_55: prefix ? Math.abs(parseFloat(prefix.computedOpacity) - 0.55) < 0.001 : false,
|
|
87
|
+
orphan_rest_state: orphan ? !orphan.pinned && !orphan.hovered : false,
|
|
88
|
+
prefix_rest_state: prefix ? !prefix.pinned && !prefix.hovered : false,
|
|
89
|
+
source_wired: sourceWired,
|
|
90
|
+
source_attr_wired: sourceAttrWired,
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} R551 orphan label opacity 0.55→0.4 (category-differentiation 4th channel):`,
|
|
94
|
+
JSON.stringify(results, null, 2),
|
|
95
|
+
'\n orphan:', JSON.stringify(orphan),
|
|
96
|
+
'\n prefix:', JSON.stringify(prefix),
|
|
97
|
+
'\n all groups:', JSON.stringify(groups, null, 2));
|
|
98
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 566 verification: panel-header count tspans (recent + legend)
|
|
2
|
+
* gain hover-state letter-spacing tween (0.2 → 0.4 on hoveredPanel).
|
|
3
|
+
* Pairs with R424/R310 hover-fw lift at the same scope.
|
|
4
|
+
*
|
|
5
|
+
* Test phases:
|
|
6
|
+
* 1. rest: data-attr 'letter-spacing' = '0.2' on both
|
|
7
|
+
* 2. source-side regex confirms both wired with hoveredPanel
|
|
8
|
+
* conditional + transition 'letter-spacing 200ms'
|
|
9
|
+
*
|
|
10
|
+
* (Live SVG hover via Playwright .hover() on the panel <g> is
|
|
11
|
+
* unreliable for state-driven attrs gated on hoveredPanel react
|
|
12
|
+
* state — banked R525 pattern; this round uses source-canonical
|
|
13
|
+
* for the gate verification and live data-attr probing for the
|
|
14
|
+
* rest state. The conditional itself was added in source.)
|
|
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: 1200 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try {
|
|
27
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
28
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
29
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
30
|
+
} catch {}
|
|
31
|
+
});
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
|
|
42
|
+
});
|
|
43
|
+
// 1 message so flowLinks > 0 → recent-panel renders
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
45
|
+
{ from_alias: 'a·1', to_alias: 'a·2', content: 'hi', created_at: fresh },
|
|
46
|
+
] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
50
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 15000 });
|
|
51
|
+
await page.waitForTimeout(500);
|
|
52
|
+
|
|
53
|
+
const probe = async () => {
|
|
54
|
+
return page.evaluate(() => ({
|
|
55
|
+
recent_ls_attr: document.querySelector('[data-recent-panel-count]')?.parentElement?.getAttribute('data-recent-panel-count-letter-spacing'),
|
|
56
|
+
legend_ls_attr: document.querySelector('[data-legend-panel-count]')?.getAttribute('data-legend-panel-count-letter-spacing'),
|
|
57
|
+
}));
|
|
58
|
+
};
|
|
59
|
+
const rest = await probe();
|
|
60
|
+
|
|
61
|
+
await browser.close();
|
|
62
|
+
|
|
63
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
64
|
+
const sourceRecentLS = /letterSpacing=\{hoveredPanel === 'recent' \? '0\.4' : '0\.2'\}/.test(src);
|
|
65
|
+
const sourceLegendLS = /letterSpacing=\{hoveredPanel === 'legend' \? '0\.4' : '0\.2'\}/.test(src);
|
|
66
|
+
const sourceRecentAttr = /data-recent-panel-count-letter-spacing=\{hoveredPanel === 'recent' \? '0\.4' : '0\.2'\}/.test(src);
|
|
67
|
+
const sourceLegendAttr = /data-legend-panel-count-letter-spacing=\{hoveredPanel === 'legend' \? '0\.4' : '0\.2'\}/.test(src);
|
|
68
|
+
const sourceRecentTransition = /transition: 'letter-spacing 200ms ease-out'/.test(src);
|
|
69
|
+
const sourceLegendTransition = /transition: 'fill 200ms ease-out, font-weight 200ms ease-out, letter-spacing 200ms ease-out'/.test(src);
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
// Rest state: data-attrs at the editorial baseline 0.2
|
|
73
|
+
rest_recent_ls_attr_0_2: rest.recent_ls_attr === '0.2',
|
|
74
|
+
rest_legend_ls_attr_0_2: rest.legend_ls_attr === '0.2',
|
|
75
|
+
// Source: both panel counts wired with conditional + transition
|
|
76
|
+
source_recent_ls: sourceRecentLS,
|
|
77
|
+
source_legend_ls: sourceLegendLS,
|
|
78
|
+
source_recent_attr: sourceRecentAttr,
|
|
79
|
+
source_legend_attr: sourceLegendAttr,
|
|
80
|
+
source_recent_transition: sourceRecentTransition,
|
|
81
|
+
source_legend_transition: sourceLegendTransition,
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} R566 panel-count hover letter-spacing (2 sibling anchors):`,
|
|
85
|
+
JSON.stringify(results, null, 2),
|
|
86
|
+
'\n rest:', JSON.stringify(rest));
|
|
87
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Round 550 verification: panel-title texts (recent + legend) gain
|
|
2
|
+
* pal.legendAccent drop-shadow on active state. 14th + 15th anchors
|
|
3
|
+
* in drop-shadow family (counted as R550 + sibling).
|
|
4
|
+
*
|
|
5
|
+
* Source-canonical for the LIVE state probe (DOM filter readback via
|
|
6
|
+
* SVG is reliable here — text element, no shared-gate IIFE). Pin
|
|
7
|
+
* trigger: click a status legend row to set pinnedStatus → glow on
|
|
8
|
+
* legend-panel title. activeEdgeKey would require flow data — for
|
|
9
|
+
* recent-panel just source-side regex confirms the wiring.
|
|
10
|
+
*
|
|
11
|
+
* Test phases:
|
|
12
|
+
* 1. rest: both panel titles' computed filter = 'none'
|
|
13
|
+
* 2. legend title — click pressure-bar working seg to pin → filter
|
|
14
|
+
* contains drop-shadow with the cyan accent
|
|
15
|
+
* 3. source regex confirms both texts wired with the filter
|
|
16
|
+
* conditional + transition list extension
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
23
|
+
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try {
|
|
29
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
30
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
31
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
32
|
+
} 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, status) => ({
|
|
39
|
+
alias, status, 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: [
|
|
44
|
+
mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
// Provide one message so flowLinks > 0 → recent-panel renders.
|
|
48
|
+
// buildFlowLinks reads from_alias/to_alias/content/created_at (banked R520).
|
|
49
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
50
|
+
{ from_alias: 'a·1', to_alias: 'a·2', content: 'hi', created_at: fresh },
|
|
51
|
+
] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
55
|
+
await page.waitForSelector('[data-legend-panel-title]', { timeout: 15000 });
|
|
56
|
+
await page.waitForTimeout(500);
|
|
57
|
+
|
|
58
|
+
// Phase 1: rest filters
|
|
59
|
+
const restLegend = await page.evaluate(() => {
|
|
60
|
+
const el = document.querySelector('[data-legend-panel-title]');
|
|
61
|
+
if (!el) return null;
|
|
62
|
+
const cs = getComputedStyle(el);
|
|
63
|
+
return { filter: cs.filter, glowAttr: el.getAttribute('data-legend-panel-title-glow') };
|
|
64
|
+
});
|
|
65
|
+
const restRecent = await page.evaluate(() => {
|
|
66
|
+
const el = document.querySelector('[data-recent-panel-title]');
|
|
67
|
+
if (!el) return null;
|
|
68
|
+
const cs = getComputedStyle(el);
|
|
69
|
+
return { filter: cs.filter, glowAttr: el.getAttribute('data-recent-panel-title-glow') };
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Phase 2: click pressure-bar working seg to pin → triggers
|
|
73
|
+
// pinnedStatus='working' → legend title should glow.
|
|
74
|
+
await page.click('[data-pressure-seg="working"]');
|
|
75
|
+
await page.waitForTimeout(400);
|
|
76
|
+
const pinnedLegend = await page.evaluate(() => {
|
|
77
|
+
const el = document.querySelector('[data-legend-panel-title]');
|
|
78
|
+
if (!el) return null;
|
|
79
|
+
const cs = getComputedStyle(el);
|
|
80
|
+
return { filter: cs.filter, glowAttr: el.getAttribute('data-legend-panel-title-glow') };
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
86
|
+
const sourceRecentFilter =
|
|
87
|
+
/filter: activeEdgeKey \? `drop-shadow\(0 0 2px \$\{pal\.legendAccent\}80\)` : undefined/.test(src);
|
|
88
|
+
const sourceLegendFilter =
|
|
89
|
+
/filter: pinnedStatus \? `drop-shadow\(0 0 2px \$\{pal\.legendAccent\}80\)` : undefined/.test(src);
|
|
90
|
+
const sourceRecentTransition =
|
|
91
|
+
/transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out, font-weight 200ms ease-out, filter 200ms ease-out'/.test(src);
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
rest_legend_filter_none: restLegend?.filter === 'none',
|
|
95
|
+
rest_recent_filter_none: restRecent?.filter === 'none',
|
|
96
|
+
rest_legend_glow_attr_false: restLegend?.glowAttr === 'false',
|
|
97
|
+
rest_recent_glow_attr_false: restRecent?.glowAttr === 'false',
|
|
98
|
+
pinned_legend_glow_attr: pinnedLegend?.glowAttr === 'true',
|
|
99
|
+
// Computed filter contains drop-shadow with rgb/rgba (hex+alpha resolves to rgba)
|
|
100
|
+
pinned_legend_filter_set: /drop-shadow\(/.test(pinnedLegend?.filter || ''),
|
|
101
|
+
source_recent_filter: sourceRecentFilter,
|
|
102
|
+
source_legend_filter: sourceLegendFilter,
|
|
103
|
+
source_recent_transition: sourceRecentTransition,
|
|
104
|
+
};
|
|
105
|
+
const ok = Object.values(results).every(Boolean);
|
|
106
|
+
console.log(`${ok ? '✅' : '❌'} R550 panel-title pin-gated drop-shadow (14th + 15th anchors):`,
|
|
107
|
+
JSON.stringify(results, null, 2),
|
|
108
|
+
'\n rest legend:', JSON.stringify(restLegend),
|
|
109
|
+
'\n rest recent:', JSON.stringify(restRecent),
|
|
110
|
+
'\n pinned legend:', JSON.stringify(pinnedLegend));
|
|
111
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* Round 547 verification: pill × close buttons gain hover:rotate-12 alongside
|
|
2
|
+
* existing hover:scale-110 (R356) + hover:opacity-70. 4 sibling buttons
|
|
3
|
+
* (status / group / vendor / edge) edited via replace_all.
|
|
4
|
+
*
|
|
5
|
+
* Test phases:
|
|
6
|
+
* 1. trigger a pin to render a pill (click pressure-bar working seg)
|
|
7
|
+
* 2. inspect the pill's × button — rest transform=none
|
|
8
|
+
* 3. hover the × button — computed transform contains rotate(12deg)
|
|
9
|
+
* AND scale(1.1)
|
|
10
|
+
* 4. source-side regex confirms hover:rotate-12 in all 4 className
|
|
11
|
+
* strings via replace_all
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
25
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
26
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
} 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('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
|
|
40
|
+
] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
46
|
+
await page.waitForSelector('[data-pressure-seg="working"]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(500);
|
|
48
|
+
|
|
49
|
+
// Phase 1: trigger pin
|
|
50
|
+
await page.click('[data-pressure-seg="working"]');
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
// Phase 2: rest read on the × button (find via aria-label).
|
|
54
|
+
// Tailwind 4 uses individual CSS `scale` / `rotate` properties (NOT
|
|
55
|
+
// transform: scale(...) rotate(...)). Probe both — banked pattern note.
|
|
56
|
+
const xSel = 'button[aria-label="Clear working filter"]';
|
|
57
|
+
await page.waitForSelector(xSel, { timeout: 5000 });
|
|
58
|
+
const rest = await page.evaluate((sel) => {
|
|
59
|
+
const el = document.querySelector(sel);
|
|
60
|
+
if (!el) return null;
|
|
61
|
+
const cs = getComputedStyle(el);
|
|
62
|
+
return { transform: cs.transform, scale: cs.scale, rotate: cs.rotate };
|
|
63
|
+
}, xSel);
|
|
64
|
+
|
|
65
|
+
// Phase 3: hover the × button
|
|
66
|
+
await page.hover(xSel);
|
|
67
|
+
await page.waitForTimeout(400);
|
|
68
|
+
const hover = await page.evaluate((sel) => {
|
|
69
|
+
const el = document.querySelector(sel);
|
|
70
|
+
if (!el) return null;
|
|
71
|
+
const cs = getComputedStyle(el);
|
|
72
|
+
return { transform: cs.transform, scale: cs.scale, rotate: cs.rotate };
|
|
73
|
+
}, xSel);
|
|
74
|
+
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
// Source regex — confirm className across all 4 instances
|
|
78
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
79
|
+
const occurrences = (src.match(/className="ml-0\.5 leading-none hover:opacity-70 transition-transform duration-200 ease-out hover:scale-110 hover:rotate-12 transform-gpu"/g) || []).length;
|
|
80
|
+
|
|
81
|
+
// Tailwind 4 emits individual `scale: ...` and `rotate: ...` CSS
|
|
82
|
+
// properties (not combined transform). Probe each axis separately.
|
|
83
|
+
const results = {
|
|
84
|
+
rest_scale_none: rest?.scale === 'none' || rest?.scale === '1' || rest?.scale === '1 1',
|
|
85
|
+
rest_rotate_none: rest?.rotate === 'none' || rest?.rotate === '0deg',
|
|
86
|
+
hover_scale_110: /\b1\.1\b/.test(hover?.scale || ''),
|
|
87
|
+
hover_rotate_12: /\b12deg\b/.test(hover?.rotate || ''),
|
|
88
|
+
source_4_occurrences: occurrences === 4,
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} R547 pill × hover-rotate:`,
|
|
92
|
+
JSON.stringify(results, null, 2),
|
|
93
|
+
'\n rest:', JSON.stringify(rest),
|
|
94
|
+
'\n hover:', JSON.stringify(hover),
|
|
95
|
+
'\n source occurrences:', occurrences);
|
|
96
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 504 verification: root svg surfaces `data-topo-pinned-aspect`
|
|
2
|
+
* categorical attribute. Sibling to R488's `data-topo-hovered-alias`
|
|
3
|
+
* (hover identity); R504 is the pin axis identity. Reflects WHICH
|
|
4
|
+
* pin state is active — paired with R467's any-pinned boolean.
|
|
5
|
+
*
|
|
6
|
+
* Tier values:
|
|
7
|
+
* none, status, group, vendor, edge, multi
|
|
8
|
+
*
|
|
9
|
+
* Test scenarios — seed pins via localStorage to reach each tier
|
|
10
|
+
* deterministically.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
17
|
+
|
|
18
|
+
async function probe({ pins }) {
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript((p) => {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
25
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
26
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
// Pins persist via sessionStorage (not localStorage) — verified
|
|
28
|
+
// by grep of TopoGraph.tsx useState initializers at lines
|
|
29
|
+
// 1033/1148/1187. pinnedEdgeKey starts null (no persistence;
|
|
30
|
+
// edge pin only reachable via click, skipped in this test).
|
|
31
|
+
if (p.status) sessionStorage.setItem('anet-topo-pinned-status', p.status);
|
|
32
|
+
if (p.group) sessionStorage.setItem('anet-topo-pinned-group', p.group);
|
|
33
|
+
if (p.vendor) sessionStorage.setItem('anet-topo-pinned-vendor', p.vendor);
|
|
34
|
+
} catch {}
|
|
35
|
+
}, pins);
|
|
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, status) => ({
|
|
41
|
+
alias, status, 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: [
|
|
46
|
+
mk('alpha·1', 'working'),
|
|
47
|
+
mk('alpha·2', 'idle'),
|
|
48
|
+
] } });
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
const page = await ctx.newPage();
|
|
53
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
54
|
+
await page.waitForSelector('svg[data-topo-pinned-aspect]', { timeout: 15000 });
|
|
55
|
+
await page.waitForTimeout(2800); // settle for localStorage-restore useEffect
|
|
56
|
+
const aspect = await page.evaluate(() => {
|
|
57
|
+
return document.querySelector('svg[viewBox="0 0 1000 680"]')?.getAttribute('data-topo-pinned-aspect');
|
|
58
|
+
});
|
|
59
|
+
await browser.close();
|
|
60
|
+
return aspect;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const none = await probe({ pins: {} });
|
|
64
|
+
const status = await probe({ pins: { status: 'working' } });
|
|
65
|
+
// Note: 'alpha' for group depends on actual alias-prefix derivation; valid
|
|
66
|
+
// because we seeded alpha·1 + alpha·2 (both have prefix 'alpha·')
|
|
67
|
+
const group = await probe({ pins: { group: 'alpha·' } });
|
|
68
|
+
// vendor uses initial letter — Anthropic = 'A' (from vendorIdentity.ts line 69)
|
|
69
|
+
// claude-opus-4 model resolves to Anthropic vendor; stale-purge keeps 'A' valid
|
|
70
|
+
const vendor = await probe({ pins: { vendor: 'A' } });
|
|
71
|
+
// edge needs from→to format; skip — edge pin keys are complex
|
|
72
|
+
// Multi: status + group together
|
|
73
|
+
const multi = await probe({ pins: { status: 'working', group: 'alpha·' } });
|
|
74
|
+
|
|
75
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
76
|
+
const sourceWired = /data-topo-pinned-aspect=\{\(\(\) => \{[\s\S]*?aspects\.push\('status'\)[\s\S]*?aspects\.push\('group'\)[\s\S]*?aspects\.push\('vendor'\)[\s\S]*?aspects\.push\('edge'\)/.test(src);
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
none_resolves: none === 'none',
|
|
80
|
+
status_resolves: status === 'status',
|
|
81
|
+
group_resolves: group === 'group',
|
|
82
|
+
vendor_resolves: vendor === 'vendor',
|
|
83
|
+
multi_resolves: multi === 'multi',
|
|
84
|
+
source_wired: sourceWired,
|
|
85
|
+
};
|
|
86
|
+
const ok = Object.values(results).every(Boolean);
|
|
87
|
+
console.log(`${ok ? '✅' : '❌'} R504 pinned-aspect:`, JSON.stringify(results),
|
|
88
|
+
'\n raw:', { none, status, group, vendor, multi });
|
|
89
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* Round 542 verification: pressure-bar segments gain drop-shadow tier-
|
|
2
|
+
* color glow on hover, stacked with R210 brightness(1.2). Chip-row
|
|
3
|
+
* tier-color glow sub-family 3rd anchor.
|
|
4
|
+
*
|
|
5
|
+
* Test phases:
|
|
6
|
+
* 1. rest: filter='none', data-pressure-seg-hovered='false'
|
|
7
|
+
* 2. hover the working seg: filter contains 'brightness(1.2)'
|
|
8
|
+
* AND 'drop-shadow(...)' with the working tier color (cyber
|
|
9
|
+
* #22c55e = rgb(34, 197, 94))
|
|
10
|
+
* 3. source-side regex confirms stacked filter syntax with hex+alpha
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
17
|
+
|
|
18
|
+
const browser = await chromium.launch({ headless: true });
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try {
|
|
23
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
24
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
25
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias, status) => ({
|
|
33
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
36
|
+
});
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
38
|
+
mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
|
|
39
|
+
] } });
|
|
40
|
+
});
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
42
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
43
|
+
const page = await ctx.newPage();
|
|
44
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
45
|
+
await page.waitForSelector('[data-pressure-seg="working"]', { timeout: 15000 });
|
|
46
|
+
await page.waitForTimeout(800);
|
|
47
|
+
|
|
48
|
+
const sel = '[data-pressure-seg="working"]';
|
|
49
|
+
|
|
50
|
+
const restRead = async () => page.evaluate((sel) => {
|
|
51
|
+
const el = document.querySelector(sel);
|
|
52
|
+
if (!el) return null;
|
|
53
|
+
const cs = getComputedStyle(el);
|
|
54
|
+
return {
|
|
55
|
+
hovered: el.getAttribute('data-pressure-seg-hovered'),
|
|
56
|
+
filter: cs.filter,
|
|
57
|
+
inlineFilter: el.style.filter,
|
|
58
|
+
};
|
|
59
|
+
}, sel);
|
|
60
|
+
|
|
61
|
+
// Phase 1: rest
|
|
62
|
+
const rest = await restRead();
|
|
63
|
+
|
|
64
|
+
// Phase 2: hover working seg
|
|
65
|
+
await page.hover(sel);
|
|
66
|
+
await page.waitForTimeout(400);
|
|
67
|
+
const hover = await restRead();
|
|
68
|
+
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
// Source regex
|
|
72
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
73
|
+
const sourceFilter =
|
|
74
|
+
/filter: hoveredStatus === key \? `brightness\(1\.2\) drop-shadow\(0 0 2px \$\{color\}99\)` : undefined,/.test(src);
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
rest_hovered_false: rest?.hovered === 'false',
|
|
78
|
+
rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
|
|
79
|
+
hover_hovered_true: hover?.hovered === 'true',
|
|
80
|
+
hover_brightness: /brightness\(1\.2\)/.test(hover?.filter || ''),
|
|
81
|
+
hover_drop_shadow: /drop-shadow/.test(hover?.filter || ''),
|
|
82
|
+
// Cyber working color #22c55e → rgb(34, 197, 94) — accept any of the
|
|
83
|
+
// computed color() forms.
|
|
84
|
+
hover_working_color: /rgba?\(34,?\s*197,?\s*94/.test(hover?.filter || ''), // R537 banked: rgba? not rgb (hex+alpha → rgba)
|
|
85
|
+
source_filter: sourceFilter,
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} R542 pressure-seg tier-color glow:`,
|
|
89
|
+
JSON.stringify(results, null, 2),
|
|
90
|
+
'\n rest:', JSON.stringify(rest),
|
|
91
|
+
'\n hover:', JSON.stringify(hover));
|
|
92
|
+
process.exit(ok ? 0 : 1);
|