@sleep2agi/agent-network-dashboard 0.5.1-preview.2 → 0.5.1-preview.21
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/0lh3wu1zlx_a5.js +1 -0
- package/.next/static/chunks/0mjn4oij3zu6y.js +4 -0
- package/.next/static/chunks/0tvbbhnjh61u8.js +1 -0
- package/.next/static/chunks/0~oj~gm3ck7o8.css +2 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +383 -43
- package/package.json +1 -1
- package/scripts/topo-active-links-freshness-color-test.mjs +86 -0
- package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
- package/scripts/topo-filter-pill-weight-test.mjs +75 -0
- package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -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-hub-digit-fontsize-test.mjs +86 -0
- package/scripts/topo-layout-toggle-hover-tracking-test.mjs +109 -0
- package/scripts/topo-minimap-viewport-hover-test.mjs +109 -0
- package/scripts/topo-more-flows-hover-letterspacing-test.mjs +98 -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-panel-title-hover-letterspacing-test.mjs +97 -0
- package/scripts/topo-recent-pip-radius-test.mjs +76 -0
- package/scripts/topo-reset-hover-rotate-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/0ap~cvzkt4_.w.js +0 -1
- package/.next/static/chunks/0bu~3a_lb2vr1.js +0 -4
- package/.next/static/chunks/14d3w289h5vr0.js +0 -1
- /package/.next/static/{U2ie3REU1St9mnDvCTXzE → OLsgiMVJR1MAtUHtDD9yq}/_buildManifest.js +0 -0
- /package/.next/static/{U2ie3REU1St9mnDvCTXzE → OLsgiMVJR1MAtUHtDD9yq}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{U2ie3REU1St9mnDvCTXzE → OLsgiMVJR1MAtUHtDD9yq}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Round 360 verification: hub working-count digit fontSize 11 → 12.
|
|
2
|
+
* The hub is the canvas's focal point — its digit is the most-read
|
|
3
|
+
* scalar on the whole topology. R130 sized it at 11 inside the
|
|
4
|
+
* r=10 / 20-px core; R360 nudges to 12 (~13 px wide × 12 px tall,
|
|
5
|
+
* still well inside) for ~9 % more presence. Sibling visual-weight
|
|
6
|
+
* bump family with R287/R295/R359.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - Hub digit font-size attr === '12'.
|
|
10
|
+
* - data-topo-hub-working-count-font-size === '12'.
|
|
11
|
+
* - data-topo-hub-working-count surfaces the count (>0 with mocked
|
|
12
|
+
* working sessions).
|
|
13
|
+
* - Pre-R360 invariants: R225 tabular-nums + R209 scale-on-hover
|
|
14
|
+
* transform-box still set, fontWeight=700, dy="0.36em" preserved.
|
|
15
|
+
* - Overlap-test stays green (digit is inside hub <g>, not a node
|
|
16
|
+
* the overlap probe scans).
|
|
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 browser = await chromium.launch({ headless: true });
|
|
23
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
27
|
+
});
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const mk = (alias) => ({
|
|
34
|
+
alias, status: 'working', 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
|
+
// 3 working sessions → workingCount=3 → digit visible.
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
40
|
+
});
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
42
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
43
|
+
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForSelector('[data-topo-hub-working-count]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
const probe = await page.evaluate(() => {
|
|
50
|
+
const t = document.querySelector('[data-topo-hub-working-count]');
|
|
51
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
52
|
+
return {
|
|
53
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
54
|
+
fontSizeData: t?.getAttribute('data-topo-hub-working-count-font-size') ?? null,
|
|
55
|
+
countValue: t?.getAttribute('data-topo-hub-working-count') ?? null,
|
|
56
|
+
fontWeight: t?.getAttribute('font-weight') ?? null,
|
|
57
|
+
dy: t?.getAttribute('dy') ?? null,
|
|
58
|
+
fontVariant: cs?.fontVariantNumeric ?? null,
|
|
59
|
+
transformBox: cs?.transformBox ?? null,
|
|
60
|
+
transformOrigin: cs?.transformOrigin ?? null,
|
|
61
|
+
transition: cs?.transition ?? null,
|
|
62
|
+
opacity: t?.getAttribute('opacity') ?? null,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await browser.close();
|
|
67
|
+
|
|
68
|
+
const hasTrans = (s, prop) =>
|
|
69
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
font_size_attr_12: probe.fontSizeAttr === '12',
|
|
73
|
+
data_font_size_12: probe.fontSizeData === '12',
|
|
74
|
+
count_value_3: probe.countValue === '3', // 3 mocked working sessions
|
|
75
|
+
font_weight_700: probe.fontWeight === '700',
|
|
76
|
+
dy_0_36em: probe.dy === '0.36em',
|
|
77
|
+
tabular_nums: /tabular-nums/.test(probe.fontVariant || ''), // R225
|
|
78
|
+
transform_box_fill: probe.transformBox === 'fill-box', // R209
|
|
79
|
+
trans_has_transform: hasTrans(probe.transition, 'transform'), // R209
|
|
80
|
+
trans_has_fill: hasTrans(probe.transition, 'fill'), // R253
|
|
81
|
+
opacity_visible: probe.opacity === '1',
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} hub digit fontSize 11 → 12:`, JSON.stringify(results),
|
|
85
|
+
'\n probe:', probe);
|
|
86
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* Round 351 verification: Layout-toggle Ring|Grid buttons gain
|
|
2
|
+
* hover-state letter-spacing (Tailwind hover:tracking-wide =
|
|
3
|
+
* 0.025em). Extends the R344 (footer) / R345 (panel titles) /
|
|
4
|
+
* R347 (zoom-level readout) hover-letter-spacing family to a
|
|
5
|
+
* 4th surface — the chrome-strip Ring/Grid layout-toggle pair.
|
|
6
|
+
*
|
|
7
|
+
* Pure-CSS approach: no new React state. transition-colors
|
|
8
|
+
* className dropped in favour of an inline transition spec
|
|
9
|
+
* that bundles bg/color (150ms ease) + letter-spacing (200ms
|
|
10
|
+
* ease-out) so the tracking-wide hover doesn't snap. Grid
|
|
11
|
+
* button additionally keeps border-color in the transition
|
|
12
|
+
* list (R268 theme-ease).
|
|
13
|
+
*
|
|
14
|
+
* Contract (cyber):
|
|
15
|
+
* - Rest: both buttons computed letter-spacing === 'normal' (0).
|
|
16
|
+
* - Hover Ring: computed letter-spacing === '0.4px' (0.025em ×
|
|
17
|
+
* ~14-16 base font; we'll accept any value > 0.1px).
|
|
18
|
+
* - Hover Grid: same.
|
|
19
|
+
* - Inline transition on both contains 'letter-spacing'.
|
|
20
|
+
* - Pre-R351 invariants: data-topo-chrome-layout attrs intact,
|
|
21
|
+
* R268 border-color transition still present on Grid.
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
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: 'working', 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('a'), mk('b'), mk('c') ] } });
|
|
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-layout="ring"]', { timeout: 15000 });
|
|
51
|
+
await page.waitForSelector('[data-topo-chrome-layout="grid"]', { timeout: 5000 });
|
|
52
|
+
await page.waitForTimeout(300);
|
|
53
|
+
|
|
54
|
+
const restProbe = await page.evaluate(() => {
|
|
55
|
+
const r = document.querySelector('[data-topo-chrome-layout="ring"]');
|
|
56
|
+
const g = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
57
|
+
const rcs = r ? getComputedStyle(r) : null;
|
|
58
|
+
const gcs = g ? getComputedStyle(g) : null;
|
|
59
|
+
return {
|
|
60
|
+
ringLs: rcs?.letterSpacing ?? null,
|
|
61
|
+
ringTrans: rcs?.transition ?? null,
|
|
62
|
+
gridLs: gcs?.letterSpacing ?? null,
|
|
63
|
+
gridTrans: gcs?.transition ?? null,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Hover Ring button.
|
|
68
|
+
await page.hover('[data-topo-chrome-layout="ring"]');
|
|
69
|
+
await page.waitForTimeout(300);
|
|
70
|
+
const ringHover = await page.evaluate(() => {
|
|
71
|
+
const r = document.querySelector('[data-topo-chrome-layout="ring"]');
|
|
72
|
+
return { ls: r ? getComputedStyle(r).letterSpacing : null };
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Move to Grid button.
|
|
76
|
+
await page.hover('[data-topo-chrome-layout="grid"]');
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
const gridHover = await page.evaluate(() => {
|
|
79
|
+
const g = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
80
|
+
return { ls: g ? getComputedStyle(g).letterSpacing : null };
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const isRestLs = (v) => v === 'normal' || v === '0px' || v === '0' || v === null;
|
|
86
|
+
const isHoverLs = (v) => {
|
|
87
|
+
// tracking-wide = 0.025em. At base 14-16px font, that's ~0.35-0.4px.
|
|
88
|
+
if (!v || v === 'normal') return false;
|
|
89
|
+
const n = parseFloat(v);
|
|
90
|
+
return Number.isFinite(n) && n > 0.2 && n < 1.0;
|
|
91
|
+
};
|
|
92
|
+
const hasTrans = (s, prop) =>
|
|
93
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
rest_ring_ls_zero: isRestLs(restProbe.ringLs),
|
|
97
|
+
rest_grid_ls_zero: isRestLs(restProbe.gridLs),
|
|
98
|
+
ring_trans_has_ls: hasTrans(restProbe.ringTrans, 'letter-spacing'),
|
|
99
|
+
grid_trans_has_ls: hasTrans(restProbe.gridTrans, 'letter-spacing'),
|
|
100
|
+
grid_trans_has_bcol: hasTrans(restProbe.gridTrans, 'border-color'), // R268
|
|
101
|
+
hover_ring_tracked: isHoverLs(ringHover.ls),
|
|
102
|
+
hover_grid_tracked: isHoverLs(gridHover.ls),
|
|
103
|
+
};
|
|
104
|
+
const ok = Object.values(results).every(Boolean);
|
|
105
|
+
console.log(`${ok ? '✅' : '❌'} layout-toggle hover tracking-wide:`, JSON.stringify(results),
|
|
106
|
+
'\n rest: ', restProbe,
|
|
107
|
+
'\n hover ring: ', ringHover,
|
|
108
|
+
'\n hover grid: ', gridHover);
|
|
109
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* Round 346 verification: minimap viewport rect gains a hover-state
|
|
2
|
+
* affordance — strokeWidth 1.5 → 1.75 + opacity 0.9 → 1.0 when the
|
|
3
|
+
* user enters the minimap container. The minimap is a recenter click-
|
|
4
|
+
* target (role=button at line ~7810), but pre-R346 the only hover hint
|
|
5
|
+
* was `cursor: crosshair`. R346 lifts the viewport rect to mark "this
|
|
6
|
+
* is the recenter target and it's alive". Sibling polish to R332
|
|
7
|
+
* minimap rounded-md → rounded-lg corner family — that round refined
|
|
8
|
+
* geometry, this one gives the viewport indicator inside the geometry
|
|
9
|
+
* a hover state.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Rest: viewport rect stroke-width=1.5, opacity=0.9.
|
|
13
|
+
* - Hover container: stroke-width=1.75, opacity=1.
|
|
14
|
+
* - Inline transition list contains 'stroke-width' AND 'opacity'.
|
|
15
|
+
* - data-topo-minimap-hovered toggles 'false' → 'true' on enter.
|
|
16
|
+
* - data-topo-minimap-viewport-hover mirrors hover state on the rect.
|
|
17
|
+
* - Pre-R346 invariants intact: rect still data-topo-minimap-viewport,
|
|
18
|
+
* R287 stroke=1.5 rest value, R199 smoothView transition list
|
|
19
|
+
* extended (not replaced).
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('alpha-1'), mk('alpha-2'), mk('alpha-3'),
|
|
43
|
+
mk('beta-1'), mk('beta-2'), mk('beta-3'),
|
|
44
|
+
mk('gamma-1'), mk('gamma-2'), mk('gamma-3'),
|
|
45
|
+
] } });
|
|
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
|
+
// Minimap only mounts when view is non-default (zoom !== 1 or pan).
|
|
53
|
+
// Click zoom-in to trigger mount.
|
|
54
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
55
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
56
|
+
await page.waitForTimeout(200);
|
|
57
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
58
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
// Rest probe.
|
|
62
|
+
const restProbe = await page.evaluate(() => {
|
|
63
|
+
const rect = document.querySelector('[data-topo-minimap-viewport]');
|
|
64
|
+
const container = document.querySelector('[data-topo-minimap]');
|
|
65
|
+
return {
|
|
66
|
+
strokeWidth: rect?.getAttribute('stroke-width') ?? null,
|
|
67
|
+
opacity: rect?.getAttribute('opacity') ?? null,
|
|
68
|
+
transition: rect ? getComputedStyle(rect).transition : null,
|
|
69
|
+
rectHoverAttr: rect?.getAttribute('data-topo-minimap-viewport-hover') ?? null,
|
|
70
|
+
contHoverAttr: container?.getAttribute('data-topo-minimap-hovered') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Hover the minimap container.
|
|
75
|
+
await page.hover('[data-topo-minimap]');
|
|
76
|
+
await page.waitForTimeout(250);
|
|
77
|
+
const hoverProbe = await page.evaluate(() => {
|
|
78
|
+
const rect = document.querySelector('[data-topo-minimap-viewport]');
|
|
79
|
+
const container = document.querySelector('[data-topo-minimap]');
|
|
80
|
+
return {
|
|
81
|
+
strokeWidth: rect?.getAttribute('stroke-width') ?? null,
|
|
82
|
+
opacity: rect?.getAttribute('opacity') ?? null,
|
|
83
|
+
rectHoverAttr: rect?.getAttribute('data-topo-minimap-viewport-hover') ?? null,
|
|
84
|
+
contHoverAttr: container?.getAttribute('data-topo-minimap-hovered') ?? null,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const hasTrans = (s, prop) =>
|
|
91
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
rest_sw_1_5: restProbe.strokeWidth === '1.5',
|
|
95
|
+
rest_op_0_9: restProbe.opacity === '0.9',
|
|
96
|
+
rest_cont_false: restProbe.contHoverAttr === 'false',
|
|
97
|
+
rest_rect_false: restProbe.rectHoverAttr === 'false',
|
|
98
|
+
trans_has_strokew: hasTrans(restProbe.transition, 'stroke-width'),
|
|
99
|
+
trans_has_opacity: hasTrans(restProbe.transition, 'opacity'),
|
|
100
|
+
hover_sw_1_75: hoverProbe.strokeWidth === '1.75',
|
|
101
|
+
hover_op_1: hoverProbe.opacity === '1',
|
|
102
|
+
hover_cont_true: hoverProbe.contHoverAttr === 'true',
|
|
103
|
+
hover_rect_true: hoverProbe.rectHoverAttr === 'true',
|
|
104
|
+
};
|
|
105
|
+
const ok = Object.values(results).every(Boolean);
|
|
106
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport hover affordance:`, JSON.stringify(results),
|
|
107
|
+
'\n rest: ', restProbe,
|
|
108
|
+
'\n hover:', hoverProbe);
|
|
109
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* Round 344 verification: +N more flows footer hover state adds
|
|
2
|
+
* letter-spacing tween 0.2 → 0.3. Layers on top of R195 cyan fill +
|
|
3
|
+
* R325 baseline letter-spacing 0.2 + R133 underline + R325 opacity
|
|
4
|
+
* 0.55 → 0.85 hover. Sibling to R218/R219/R220 pin-signature letter-
|
|
5
|
+
* spacing family applied to a hover-only surface.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Rest state: [data-recent-panel-more] letter-spacing="0.2".
|
|
9
|
+
* - Hover state: letter-spacing="0.3" (after mouseover).
|
|
10
|
+
* - Inline style.transition contains 'letter-spacing'.
|
|
11
|
+
* - R325 + R259 regressions intact.
|
|
12
|
+
* - R317/R318 chrome regression.
|
|
13
|
+
*/
|
|
14
|
+
import { chromium } from 'playwright';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
18
|
+
const browser = await chromium.launch({ headless: true });
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias) => ({
|
|
30
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
35
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'), mk('epsilon'),
|
|
36
|
+
] } });
|
|
37
|
+
});
|
|
38
|
+
// 5 flow pairs → footer mounts (need >3 flowLinks).
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const mkMsg = (idx, from_alias, to_alias) => ({
|
|
41
|
+
id: `${from_alias}-${to_alias}-${idx}`,
|
|
42
|
+
from_alias, to_alias, content: `m${idx}`,
|
|
43
|
+
network_id: 'default',
|
|
44
|
+
created_at: new Date(now - (5 + idx) * 1000).toISOString(),
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
47
|
+
mkMsg(1, 'alpha', 'beta'),
|
|
48
|
+
mkMsg(2, 'beta', 'gamma'),
|
|
49
|
+
mkMsg(3, 'gamma', 'delta'),
|
|
50
|
+
mkMsg(4, 'delta', 'epsilon'),
|
|
51
|
+
mkMsg(5, 'epsilon', 'alpha'),
|
|
52
|
+
] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
// Rest-state probe.
|
|
61
|
+
const restProbe = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-recent-panel-more]');
|
|
63
|
+
return {
|
|
64
|
+
letterSpacing: el?.getAttribute('letter-spacing') ?? null,
|
|
65
|
+
transition: el ? getComputedStyle(el).transition : null,
|
|
66
|
+
hovered: el?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Hover the parent <g> wrapper — the onMouseEnter is on the link group.
|
|
71
|
+
await page.hover('[data-recent-panel-more-nav]');
|
|
72
|
+
await page.waitForTimeout(300);
|
|
73
|
+
|
|
74
|
+
const hoverProbe = await page.evaluate(() => {
|
|
75
|
+
const el = document.querySelector('[data-recent-panel-more]');
|
|
76
|
+
return {
|
|
77
|
+
letterSpacing: el?.getAttribute('letter-spacing') ?? null,
|
|
78
|
+
hovered: el?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const hasLetterSpacingTrans = (s) =>
|
|
85
|
+
/letter-spacing\s+\d*\.?\d*s|letter-spacing\s+\d+ms/i.test(s || '');
|
|
86
|
+
|
|
87
|
+
const results = {
|
|
88
|
+
rest_letter_spacing_0_2: restProbe.letterSpacing === '0.2',
|
|
89
|
+
rest_not_hovered: restProbe.hovered === 'false',
|
|
90
|
+
hover_letter_spacing_0_3: hoverProbe.letterSpacing === '0.3',
|
|
91
|
+
hover_state_true: hoverProbe.hovered === 'true',
|
|
92
|
+
transition_has_letter_sp: hasLetterSpacingTrans(restProbe.transition),
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} more-flows hover letter-spacing:`, JSON.stringify(results),
|
|
96
|
+
'\n rest:', restProbe,
|
|
97
|
+
'\n hover:', hoverProbe);
|
|
98
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 349 verification: panel header counts pick up editorial
|
|
2
|
+
* letter-spacing 0.2, one tier below the R301 panel title 0.3.
|
|
3
|
+
* Closes the panel-pair editorial symmetry — both top-corner panels
|
|
4
|
+
* now have a 2-step header hierarchy:
|
|
5
|
+
* title fontSize=12 fontWeight=700 letterSpacing=0.3 (R301)
|
|
6
|
+
* count fontSize=10 fontWeight=600 letterSpacing=0.2 (R349, this round)
|
|
7
|
+
* Joins the R285 / R289 / R301 / R302 / R304 / R325 editorial-
|
|
8
|
+
* letterspacing tier at the panel-summary scope. The R162 freshness
|
|
9
|
+
* fill, R225 tabular-nums, R311 fontWeight, R336 unit-tspan opacity-0.7
|
|
10
|
+
* split all preserved (SVG inheritance propagates the tier to descendant
|
|
11
|
+
* tspans).
|
|
12
|
+
*
|
|
13
|
+
* Contract:
|
|
14
|
+
* - Recent-signal panel header count <text> letter-spacing="0.2".
|
|
15
|
+
* - Legend panel header count <text> letter-spacing="0.2".
|
|
16
|
+
* - data-{recent,legend}-panel-count-letter-spacing="0.2" attrs present.
|
|
17
|
+
* - Pre-R349 invariants: R311 fontWeight=600, R225 tabular-nums,
|
|
18
|
+
* R336 unit-tspan opacity=0.7 all preserved.
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
41
|
+
});
|
|
42
|
+
// One message → recent panel mounts.
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
45
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
46
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
47
|
+
] } }));
|
|
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-recent-panel-count]', { timeout: 15000 });
|
|
53
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const rTspan = document.querySelector('[data-recent-panel-count]');
|
|
58
|
+
const rText = rTspan?.closest('text');
|
|
59
|
+
const rUnit = document.querySelector('[data-recent-panel-count-unit]');
|
|
60
|
+
const lText = document.querySelector('[data-legend-panel-count]');
|
|
61
|
+
const lUnit = document.querySelector('[data-legend-panel-count-unit]');
|
|
62
|
+
return {
|
|
63
|
+
recentTextLs: rText?.getAttribute('letter-spacing') ?? null,
|
|
64
|
+
recentLsAttr: rText?.getAttribute('data-recent-panel-count-letter-spacing') ?? null,
|
|
65
|
+
recentFw: rTspan?.getAttribute('font-weight') ?? null,
|
|
66
|
+
recentUnitOp: rUnit?.getAttribute('opacity') ?? null,
|
|
67
|
+
legendTextLs: lText?.getAttribute('letter-spacing') ?? null,
|
|
68
|
+
legendLsAttr: lText?.getAttribute('data-legend-panel-count-letter-spacing') ?? null,
|
|
69
|
+
legendFw: lText?.getAttribute('font-weight') ?? null,
|
|
70
|
+
legendUnitOp: lUnit?.getAttribute('opacity') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
recent_text_ls_0_2: probe.recentTextLs === '0.2',
|
|
78
|
+
recent_attr_0_2: probe.recentLsAttr === '0.2',
|
|
79
|
+
recent_fw_600: probe.recentFw === '600', // R311
|
|
80
|
+
recent_unit_op_0_7: probe.recentUnitOp === '0.7', // R336
|
|
81
|
+
legend_text_ls_0_2: probe.legendTextLs === '0.2',
|
|
82
|
+
legend_attr_0_2: probe.legendLsAttr === '0.2',
|
|
83
|
+
legend_fw_600: probe.legendFw === '600', // R310
|
|
84
|
+
legend_unit_op_0_7: probe.legendUnitOp === '0.7', // R336
|
|
85
|
+
};
|
|
86
|
+
const ok = Object.values(results).every(Boolean);
|
|
87
|
+
console.log(`${ok ? '✅' : '❌'} panel count letter-spacing 0.2:`, JSON.stringify(results),
|
|
88
|
+
'\n probe:', probe);
|
|
89
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* Round 348 verification: panel rect opacity hover-state bump.
|
|
2
|
+
* Recent-signal + legend panels both lift their fill opacity when
|
|
3
|
+
* hoveredPanel === <self>. Cyber: 0.92 → 0.97. Light: 0.97 → 1.0.
|
|
4
|
+
* Joins the panel-hover cue stack:
|
|
5
|
+
* - R135 drop-shadow boost
|
|
6
|
+
* - R345 panel title letter-spacing tween 0.3 → 0.4
|
|
7
|
+
* - R266 fill theme-flip
|
|
8
|
+
* - R348 rect opacity bump (this round)
|
|
9
|
+
* Pure paint-level change — geometry-safe, bbox unchanged, so
|
|
10
|
+
* topo-overlap-test invariants hold. The R247 transition list already
|
|
11
|
+
* includes `opacity 200ms ease-out` so the value tween is automatic.
|
|
12
|
+
*
|
|
13
|
+
* Contract (cyber theme):
|
|
14
|
+
* - Rest: both panel rects opacity=0.92.
|
|
15
|
+
* - Hover recent: recent-panel rect opacity=0.97.
|
|
16
|
+
* - Hover legend: legend-panel rect opacity=0.97.
|
|
17
|
+
* - data-topo-panel-elevation selectors locate the rects (existing).
|
|
18
|
+
* - data-topo-panel-hovered toggles on parent <g> (existing R135).
|
|
19
|
+
* - Pre-R348 invariants: R331 rx=10 + R247 transition list intact.
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
|
|
42
|
+
});
|
|
43
|
+
// One message so the recent-signal panel mounts.
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
46
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping',
|
|
47
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
48
|
+
] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForSelector('[data-topo-panel-elevation="recent"]', { timeout: 15000 });
|
|
54
|
+
await page.waitForSelector('[data-topo-panel-elevation="legend"]', { timeout: 5000 });
|
|
55
|
+
await page.waitForTimeout(400);
|
|
56
|
+
|
|
57
|
+
// Rest probe (cyber theme).
|
|
58
|
+
const restProbe = await page.evaluate(() => {
|
|
59
|
+
const r = document.querySelector('[data-topo-panel-elevation="recent"]');
|
|
60
|
+
const l = document.querySelector('[data-topo-panel-elevation="legend"]');
|
|
61
|
+
return {
|
|
62
|
+
recentOpacity: r?.getAttribute('opacity') ?? null,
|
|
63
|
+
legendOpacity: l?.getAttribute('opacity') ?? null,
|
|
64
|
+
recentRx: r?.getAttribute('rx') ?? null,
|
|
65
|
+
legendRx: l?.getAttribute('rx') ?? null,
|
|
66
|
+
recentTrans: r ? getComputedStyle(r).transition : null,
|
|
67
|
+
legendTrans: l ? getComputedStyle(l).transition : null,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Hover recent panel.
|
|
72
|
+
await page.hover('[data-topo-panel="recent"]');
|
|
73
|
+
await page.waitForTimeout(300);
|
|
74
|
+
const recentHoverProbe = await page.evaluate(() => {
|
|
75
|
+
const r = document.querySelector('[data-topo-panel-elevation="recent"]');
|
|
76
|
+
return { opacity: r?.getAttribute('opacity') ?? null };
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Hover legend panel.
|
|
80
|
+
await page.hover('[data-topo-panel="legend"]');
|
|
81
|
+
await page.waitForTimeout(300);
|
|
82
|
+
const legendHoverProbe = await page.evaluate(() => {
|
|
83
|
+
const l = document.querySelector('[data-topo-panel-elevation="legend"]');
|
|
84
|
+
return { opacity: l?.getAttribute('opacity') ?? null };
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await browser.close();
|
|
88
|
+
|
|
89
|
+
const hasTrans = (s, prop) =>
|
|
90
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
91
|
+
|
|
92
|
+
const results = {
|
|
93
|
+
rest_recent_0_92: restProbe.recentOpacity === '0.92',
|
|
94
|
+
rest_legend_0_92: restProbe.legendOpacity === '0.92',
|
|
95
|
+
rest_recent_rx_10: restProbe.recentRx === '10',
|
|
96
|
+
rest_legend_rx_10: restProbe.legendRx === '10',
|
|
97
|
+
trans_recent_op: hasTrans(restProbe.recentTrans, 'opacity'),
|
|
98
|
+
trans_legend_op: hasTrans(restProbe.legendTrans, 'opacity'),
|
|
99
|
+
trans_recent_filter: hasTrans(restProbe.recentTrans, 'filter'),
|
|
100
|
+
trans_legend_filter: hasTrans(restProbe.legendTrans, 'filter'),
|
|
101
|
+
hover_recent_0_97: recentHoverProbe.opacity === '0.97',
|
|
102
|
+
hover_legend_0_97: legendHoverProbe.opacity === '0.97',
|
|
103
|
+
};
|
|
104
|
+
const ok = Object.values(results).every(Boolean);
|
|
105
|
+
console.log(`${ok ? '✅' : '❌'} panel rect opacity hover bump:`, JSON.stringify(results),
|
|
106
|
+
'\n rest: ', restProbe,
|
|
107
|
+
'\n hover recent:', recentHoverProbe,
|
|
108
|
+
'\n hover legend:', legendHoverProbe);
|
|
109
|
+
process.exit(ok ? 0 : 1);
|