@sleep2agi/agent-network-dashboard 0.5.1-preview.7 → 0.5.1-preview.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +6 -6
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +2 -2
- package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +2 -2
- package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +2 -2
- package/.next/server/app/node.segments/_full.segment.rsc +2 -2
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +2 -2
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +2 -2
- package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +2 -2
- package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +3 -3
- package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +2 -2
- package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/04.j~y8e~sbs4.js +1 -0
- package/.next/static/chunks/06llamqb4jsu..js +4 -0
- package/.next/static/chunks/0_p8jkzdw5x2_.css +2 -0
- package/.next/static/chunks/12heglqfrp1bm.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +1426 -84
- package/package.json +1 -1
- package/scripts/topo-active-links-chip-hover-lift-test.mjs +93 -0
- package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
- package/scripts/topo-chip-row-hover-lift-test.mjs +95 -0
- package/scripts/topo-chrome-button-hover-lift-test.mjs +94 -0
- package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
- package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
- package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
- package/scripts/topo-edge-badge-hover-opacity-test.mjs +94 -0
- package/scripts/topo-edge-badge-hover-stroke-test.mjs +92 -0
- package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
- package/scripts/topo-edge-badge-pin-opacity-test.mjs +86 -0
- package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
- package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
- package/scripts/topo-filter-pill-hover-lift-test.mjs +101 -0
- package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
- package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
- package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
- package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
- package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
- package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
- package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
- package/scripts/topo-fullscreen-icon-hover-scale-test.mjs +91 -0
- package/scripts/topo-group-box-stroke-test.mjs +105 -0
- package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
- package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
- package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
- package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
- package/scripts/topo-hover-detail-rx-test.mjs +81 -0
- package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
- package/scripts/topo-hub-halo-light-trough-test.mjs +88 -0
- package/scripts/topo-hub-halo-radius-test.mjs +86 -0
- package/scripts/topo-hub-halo-trough-test.mjs +83 -0
- package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
- package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
- package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
- package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
- package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
- package/scripts/topo-layout-toggle-hover-tracking-test.mjs +109 -0
- package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
- package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
- package/scripts/topo-legend-pin-ring-stroke-test.mjs +101 -0
- package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
- package/scripts/topo-minimap-online-opacity-test.mjs +93 -0
- package/scripts/topo-minimap-online-radius-test.mjs +85 -0
- package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
- package/scripts/topo-minimap-viewport-rx-test.mjs +85 -0
- package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
- package/scripts/topo-node-halo-offline-opacity-test.mjs +87 -0
- package/scripts/topo-node-pulse-peak-test.mjs +89 -0
- package/scripts/topo-panel-count-letterspacing-test.mjs +89 -0
- package/scripts/topo-panel-rect-opacity-hover-test.mjs +109 -0
- package/scripts/topo-pressure-bar-height-test.mjs +92 -0
- package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
- package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
- package/scripts/topo-recent-pip-radius-test.mjs +76 -0
- package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
- package/scripts/topo-reset-hover-rotate-test.mjs +102 -0
- package/scripts/topo-spoke-active-opacity-test.mjs +104 -0
- package/scripts/topo-vendor-chip-hover-lift-test.mjs +87 -0
- package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
- package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
- package/scripts/topo-zoom-icon-hover-scale-test.mjs +114 -0
- package/scripts/topo-zoom-level-hover-letterspacing-test.mjs +91 -0
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/0bja1amnrg3li.js +0 -1
- package/.next/static/chunks/0k~uc0~~19hyy.js +0 -4
- package/.next/static/chunks/0wtq_6dnzems6.js +0 -1
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_buildManifest.js +0 -0
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{x9zCCrMkHsIYlXNY791KF → gaK6yNvVjshUCmKR9qrPn}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* Round 383 verification: recency pip base radius 1.8 → 2.0.
|
|
2
|
+
* Continues R359 lift trajectory; sibling visual-weight bump
|
|
3
|
+
* family (9th anchor).
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Pip element computed r === '2.0' (rendered) or attribute equals 2.
|
|
7
|
+
* - data-recent-row-freshness-radius === '2.0'.
|
|
8
|
+
* - Pre-R383 invariants:
|
|
9
|
+
* * cx=10 (R160 left margin position unchanged)
|
|
10
|
+
* * fill=pal.legendAccent
|
|
11
|
+
* * opacity transition (R358) preserved
|
|
12
|
+
* * R358 stale-alpha floor 0.30 still applies
|
|
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: [ mk('a'), mk('b') ] } });
|
|
35
|
+
});
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
38
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
39
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString(),
|
|
40
|
+
last_at: new Date(now - 5000).toISOString() },
|
|
41
|
+
] } }));
|
|
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-recent-row-freshness]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
const probe = await page.evaluate(() => {
|
|
50
|
+
const pip = document.querySelector('[data-recent-row-freshness]');
|
|
51
|
+
return {
|
|
52
|
+
rAttr: pip?.getAttribute('r') ?? null,
|
|
53
|
+
radiusAttr: pip?.getAttribute('data-recent-row-freshness-radius') ?? null,
|
|
54
|
+
cxAttr: pip?.getAttribute('cx') ?? null,
|
|
55
|
+
linkKey: pip?.getAttribute('data-recent-row-freshness') ?? null,
|
|
56
|
+
opacityAttr: pip?.getAttribute('opacity') ?? null,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await browser.close();
|
|
61
|
+
|
|
62
|
+
const results = {
|
|
63
|
+
r_attr_2: probe.rAttr === '2' || probe.rAttr === '2.0',
|
|
64
|
+
radius_attr_2_0: probe.radiusAttr === '2.0',
|
|
65
|
+
cx_unchanged_10: probe.cxAttr === '10', // R160 invariant
|
|
66
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
67
|
+
opacity_above_zero: parseFloat(probe.opacityAttr || '0') > 0, // fresh, alpha > 0
|
|
68
|
+
};
|
|
69
|
+
const ok = Object.values(results).every(Boolean);
|
|
70
|
+
console.log(`${ok ? '✅' : '❌'} recent pip radius 1.8 → 2.0:`, JSON.stringify(results),
|
|
71
|
+
'\n probe:', probe);
|
|
72
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* Round 359 verification: recency pip base radius 1.6 → 1.8. Sibling
|
|
2
|
+
* to R358 freshness floor lift (alpha 0.25 → 0.30) — together the
|
|
3
|
+
* stale pip stays distinguishable as it ages.
|
|
4
|
+
* pre-R358/R359: stale pip painted at r=1.6 + α=0.25 (near-invisible)
|
|
5
|
+
* post-R358: stale pip α=0.30
|
|
6
|
+
* post-R359: stale pip r=1.8 (1.8² / 1.6² ≈ 1.27, ~27% more glyph)
|
|
7
|
+
*
|
|
8
|
+
* Same visual-weight family as R287 minimap stroke 1 → 1.5 and R295
|
|
9
|
+
* legend swatch base radius 5.5 → 6.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Pip element computed r === '1.8' (or rendered SVG r attr).
|
|
13
|
+
* - data-recent-row-freshness-radius === '1.8'.
|
|
14
|
+
* - Pre-R359 invariants preserved:
|
|
15
|
+
* * R358 stale-alpha floor 0.30 still applies (visible on > 300s)
|
|
16
|
+
* * data-recent-row-freshness attr still surfaces the link key
|
|
17
|
+
* * Pip stays inside the 7-px left margin (cx=10 unchanged)
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias) => ({
|
|
35
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
|
|
40
|
+
});
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
43
|
+
// Fresh message → row with pip mounts.
|
|
44
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
45
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString(),
|
|
46
|
+
last_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-row-freshness]', { timeout: 15000 });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = await page.evaluate(() => {
|
|
56
|
+
const pip = document.querySelector('[data-recent-row-freshness]');
|
|
57
|
+
return {
|
|
58
|
+
rAttr: pip?.getAttribute('r') ?? null,
|
|
59
|
+
radiusAttr: pip?.getAttribute('data-recent-row-freshness-radius') ?? null,
|
|
60
|
+
cxAttr: pip?.getAttribute('cx') ?? null,
|
|
61
|
+
linkKey: pip?.getAttribute('data-recent-row-freshness') ?? null,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await browser.close();
|
|
66
|
+
|
|
67
|
+
const results = {
|
|
68
|
+
r_attr_1_8: probe.rAttr === '1.8',
|
|
69
|
+
radius_attr_1_8: probe.radiusAttr === '1.8',
|
|
70
|
+
cx_unchanged_10: probe.cxAttr === '10', // R160 margin invariant
|
|
71
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
72
|
+
};
|
|
73
|
+
const ok = Object.values(results).every(Boolean);
|
|
74
|
+
console.log(`${ok ? '✅' : '❌'} recent pip radius 1.6 → 1.8:`, JSON.stringify(results),
|
|
75
|
+
'\n probe:', probe);
|
|
76
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 363 verification: recent-signal row text fontWeight 400 → 500.
|
|
2
|
+
* At fontSize=9 the default-weight text was thin; R363 lifts to the
|
|
3
|
+
* font-medium tier (500) for better legibility against the panel
|
|
4
|
+
* chrome. R320 count tspan fw 600/700 override stays local, so the
|
|
5
|
+
* count-vs-alias hierarchy holds:
|
|
6
|
+
* alias fw 500 (R363)
|
|
7
|
+
* count fw 600/700 (R320)
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - data-recent-row-text element font-weight attr === '500'.
|
|
11
|
+
* - data-recent-row-text-font-weight === '500'.
|
|
12
|
+
* - The inner count tspan still has fw 600 (cold flow, R320 default).
|
|
13
|
+
* - Pre-R363 invariants:
|
|
14
|
+
* * fontSize=9 + fontFamily=monospace
|
|
15
|
+
* * R220 letter-spacing pin tween still wired (style.transition
|
|
16
|
+
* contains letter-spacing)
|
|
17
|
+
* * data-recent-row-text key surfaces the link key
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias) => ({
|
|
35
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
|
|
40
|
+
});
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
// 3 messages on the same link → count = 3 (cold, below the 10 hot threshold).
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
44
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
45
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
46
|
+
{ id: 'm2', from_alias: 'a', to_alias: 'b', content: 'pong',
|
|
47
|
+
network_id: 'default', created_at: new Date(now - 4000).toISOString() },
|
|
48
|
+
{ id: 'm3', from_alias: 'a', to_alias: 'b', content: 'ping2',
|
|
49
|
+
network_id: 'default', created_at: new Date(now - 3000).toISOString() },
|
|
50
|
+
] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForSelector('[data-recent-row-text]', { timeout: 15000 });
|
|
56
|
+
await page.waitForTimeout(300);
|
|
57
|
+
|
|
58
|
+
const probe = await page.evaluate(() => {
|
|
59
|
+
const t = document.querySelector('[data-recent-row-text]');
|
|
60
|
+
const count = document.querySelector('[data-recent-row-count]');
|
|
61
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
62
|
+
return {
|
|
63
|
+
fontWeightAttr: t?.getAttribute('font-weight') ?? null,
|
|
64
|
+
fontWeightData: t?.getAttribute('data-recent-row-text-font-weight') ?? null,
|
|
65
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
66
|
+
fontFamily: t?.getAttribute('font-family') ?? null,
|
|
67
|
+
linkKey: t?.getAttribute('data-recent-row-text') ?? null,
|
|
68
|
+
transition: cs?.transition ?? null,
|
|
69
|
+
countFwAttr: count?.getAttribute('font-weight') ?? null, // R320 override
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const hasTrans = (s, prop) =>
|
|
76
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
font_weight_500: probe.fontWeightAttr === '500',
|
|
80
|
+
data_fw_500: probe.fontWeightData === '500',
|
|
81
|
+
font_size_9: probe.fontSizeAttr === '9',
|
|
82
|
+
font_family_mono: /monospace/i.test(probe.fontFamily || ''),
|
|
83
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
84
|
+
trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'), // R220
|
|
85
|
+
count_fw_600: probe.countFwAttr === '600', // R320 cold default
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} recent-row text fw 400 → 500:`, JSON.stringify(results),
|
|
89
|
+
'\n probe:', probe);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* Round 350 verification: chrome reset-button icon gains a hover-
|
|
2
|
+
* rotate preview of the R184 click-spin. Pre-R350 hovering the reset
|
|
3
|
+
* button only changed the button bg (white/5); the icon stayed still.
|
|
4
|
+
* R350 nudges the icon -8° on hover — tactile hint that this button
|
|
5
|
+
* rotates the icon on click. Gated on !resetSpinning so anet-reset-
|
|
6
|
+
* spin keyframe still owns transform during its 450 ms run.
|
|
7
|
+
* 350th-round milestone polish.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - Rest: icon transform 'rotate(0deg)' (no rotation), transformOrigin
|
|
11
|
+
* 'center'.
|
|
12
|
+
* - Hover: icon transform 'rotate(-8deg)'.
|
|
13
|
+
* - Inline transition contains 'transform'.
|
|
14
|
+
* - data-topo-chrome-reset-hover toggles on container; data-topo-
|
|
15
|
+
* chrome-reset-icon-hover mirrors on the icon (only when !spinning).
|
|
16
|
+
* - Pre-R350 invariants: R288 strokeWidth=2.5 + R184 anet-reset-spin
|
|
17
|
+
* className gate intact (no spin running at rest).
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias) => ({
|
|
35
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), 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-chrome-reset]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
// Rest probe.
|
|
50
|
+
const restProbe = await page.evaluate(() => {
|
|
51
|
+
const btn = document.querySelector('[data-topo-chrome-reset]');
|
|
52
|
+
const ico = document.querySelector('[data-topo-chrome-reset-icon]');
|
|
53
|
+
const cs = ico ? getComputedStyle(ico) : null;
|
|
54
|
+
return {
|
|
55
|
+
btnHover: btn?.getAttribute('data-topo-chrome-reset-hover') ?? null,
|
|
56
|
+
icoHover: ico?.getAttribute('data-topo-chrome-reset-icon-hover') ?? null,
|
|
57
|
+
icoTransform: cs?.transform ?? null,
|
|
58
|
+
icoTransition: cs?.transition ?? null,
|
|
59
|
+
icoStrokeW: ico?.getAttribute('stroke-width') ?? null,
|
|
60
|
+
spinClass: ico?.getAttribute('class') ?? null,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Hover the reset button.
|
|
65
|
+
await page.hover('[data-topo-chrome-reset]');
|
|
66
|
+
await page.waitForTimeout(300);
|
|
67
|
+
const hoverProbe = await page.evaluate(() => {
|
|
68
|
+
const btn = document.querySelector('[data-topo-chrome-reset]');
|
|
69
|
+
const ico = document.querySelector('[data-topo-chrome-reset-icon]');
|
|
70
|
+
const cs = ico ? getComputedStyle(ico) : null;
|
|
71
|
+
return {
|
|
72
|
+
btnHover: btn?.getAttribute('data-topo-chrome-reset-hover') ?? null,
|
|
73
|
+
icoHover: ico?.getAttribute('data-topo-chrome-reset-icon-hover') ?? null,
|
|
74
|
+
icoTransform: cs?.transform ?? null,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const hasTrans = (s) => /transform\s+\d*\.?\d*s|transform\s+\d+ms/i.test(s || '');
|
|
81
|
+
// computed transform 'none' or a matrix near identity at rest.
|
|
82
|
+
const isRestTransform = (t) => t === 'none' || /matrix\(1, 0, 0, 1, 0, 0\)/.test(t || '') ||
|
|
83
|
+
/matrix\(0\.999/i.test(t || '') /* tiny rounding tolerance */;
|
|
84
|
+
// computed transform for -8deg ≈ matrix(0.990, -0.139, 0.139, 0.990, 0, 0).
|
|
85
|
+
const isHoverTransform = (t) => /matrix\(0\.99/i.test(t || '') && /-0\.13/i.test(t || '');
|
|
86
|
+
|
|
87
|
+
const results = {
|
|
88
|
+
rest_btn_false: restProbe.btnHover === 'false',
|
|
89
|
+
rest_ico_false: restProbe.icoHover === 'false',
|
|
90
|
+
rest_no_rotation: isRestTransform(restProbe.icoTransform),
|
|
91
|
+
trans_has_transform: hasTrans(restProbe.icoTransition),
|
|
92
|
+
rest_strokew_2_5: restProbe.icoStrokeW === '2.5', // R288
|
|
93
|
+
rest_no_spin_class: !/anet-reset-spin/.test(restProbe.spinClass || ''),
|
|
94
|
+
hover_btn_true: hoverProbe.btnHover === 'true',
|
|
95
|
+
hover_ico_true: hoverProbe.icoHover === 'true',
|
|
96
|
+
hover_rotated: isHoverTransform(hoverProbe.icoTransform),
|
|
97
|
+
};
|
|
98
|
+
const ok = Object.values(results).every(Boolean);
|
|
99
|
+
console.log(`${ok ? '✅' : '❌'} reset icon hover-rotate:`, JSON.stringify(results),
|
|
100
|
+
'\n rest: ', restProbe,
|
|
101
|
+
'\n hover:', hoverProbe);
|
|
102
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Round 391 verification: hub-spoke active opacity 0.7 → 0.8. An
|
|
2
|
+
* "active" spoke is one whose alias has recent message traffic
|
|
3
|
+
* (activeAliases is populated from buildFlowLinks(messages)). The
|
|
4
|
+
* lift unifies active-spoke alpha with R370 hub hover-ring opacity
|
|
5
|
+
* 0.7 → 0.8 cyber — paired canvas signals at matching alpha.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - When the alias appears as flow-link from/to, its spoke has:
|
|
9
|
+
* * opacity attr === '0.8'
|
|
10
|
+
* * data-topo-hub-spoke-opacity === '0.8'
|
|
11
|
+
* * data-topo-hub-spoke-active === 'true'
|
|
12
|
+
* - Idle spokes (no flow traffic) unchanged:
|
|
13
|
+
* * opacity attr === '0.45'
|
|
14
|
+
* * data-topo-hub-spoke-active === 'false'
|
|
15
|
+
* - Pre-R391 invariants preserved on the active spoke:
|
|
16
|
+
* * R382 strokeLinecap='round'
|
|
17
|
+
* * R51-safe strokeWidth=2 (active)
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
|
|
41
|
+
});
|
|
42
|
+
// Seed a flow link between alpha and beta so their spokes become
|
|
43
|
+
// active; gamma stays idle. activeAliases = { alpha, beta }.
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
45
|
+
json: {
|
|
46
|
+
messages: [
|
|
47
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
48
|
+
{ id: 'm2', from_alias: 'alpha', to_alias: 'beta', content: 'pong', created_at: fresh, network_id: 'default' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForSelector('g[data-node]', { timeout: 15000 });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
// Wait for the dashboard to ingest the seeded messages and recompute
|
|
60
|
+
// activeAliases. The flow-links derivation runs in a useMemo that
|
|
61
|
+
// depends on `messages`; allow a tick for SWR/state propagation.
|
|
62
|
+
await page.waitForTimeout(800);
|
|
63
|
+
|
|
64
|
+
const probe = await page.evaluate(() => {
|
|
65
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
66
|
+
const active = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'true');
|
|
67
|
+
const idle = spokes.filter((s) => s.getAttribute('data-topo-hub-spoke-active') === 'false');
|
|
68
|
+
const read = (el) => el ? ({
|
|
69
|
+
opacityAttr: el.getAttribute('opacity'),
|
|
70
|
+
opacityData: el.getAttribute('data-topo-hub-spoke-opacity'),
|
|
71
|
+
strokeWidth: el.getAttribute('stroke-width'),
|
|
72
|
+
linecap: el.getAttribute('stroke-linecap'),
|
|
73
|
+
}) : null;
|
|
74
|
+
return {
|
|
75
|
+
total: spokes.length,
|
|
76
|
+
activeCount: active.length,
|
|
77
|
+
idleCount: idle.length,
|
|
78
|
+
activeSample: read(active[0]),
|
|
79
|
+
idleSample: read(idle[0]),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const results = {
|
|
86
|
+
// 3 spokes total (alpha, beta, gamma)
|
|
87
|
+
total_3_spokes: probe.total === 3,
|
|
88
|
+
// alpha + beta active (flow link)
|
|
89
|
+
active_2_spokes: probe.activeCount === 2,
|
|
90
|
+
idle_1_spoke: probe.idleCount === 1,
|
|
91
|
+
// R391: active opacity '0.8' (was '0.7')
|
|
92
|
+
active_opacity_0_8: probe.activeSample?.opacityAttr === '0.8',
|
|
93
|
+
active_data_0_8: probe.activeSample?.opacityData === '0.8',
|
|
94
|
+
// Idle invariant
|
|
95
|
+
idle_opacity_0_45: probe.idleSample?.opacityAttr === '0.45',
|
|
96
|
+
idle_data_0_45: probe.idleSample?.opacityData === '0.45',
|
|
97
|
+
// R382 + R51-safe invariants on active spoke
|
|
98
|
+
active_linecap_round: probe.activeSample?.linecap === 'round',
|
|
99
|
+
active_strokeWidth_2: probe.activeSample?.strokeWidth === '2',
|
|
100
|
+
};
|
|
101
|
+
const ok = Object.values(results).every(Boolean);
|
|
102
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke active opacity 0.7 → 0.8:`, JSON.stringify(results),
|
|
103
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
104
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 401 verification: vendor letter chips close the hover-lift
|
|
2
|
+
* gesture family at the last interactive HTML chip surface. Sibling
|
|
3
|
+
* to R397 filter pin pills + R398/R399 chip-row chips + R400 chrome
|
|
4
|
+
* standalone buttons.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - Vendor letter chip mounted with at least one vendor present:
|
|
8
|
+
* * data-vendor-letter-hover-lift === 'true'
|
|
9
|
+
* * className includes 'hover:-translate-y-px'
|
|
10
|
+
* * className includes 'transition-transform' + 'transform-gpu'
|
|
11
|
+
* - Pre-R401 invariants preserved:
|
|
12
|
+
* * tabular-nums + font-medium + anet-topo-chip-focus
|
|
13
|
+
* * data-vendor-letter / -count / -pinned / -hovered attrs present
|
|
14
|
+
*/
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
19
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
20
|
+
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
26
|
+
});
|
|
27
|
+
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, model) => ({
|
|
32
|
+
alias, status: 'idle', model, 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', 'claude-opus-4'),
|
|
38
|
+
mk('beta', 'gpt-5'),
|
|
39
|
+
mk('gamma', 'gemini-2.0-pro'),
|
|
40
|
+
] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('[data-vendor-letter-hover-lift="true"]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const probe = await page.evaluate(() => {
|
|
51
|
+
const chips = Array.from(document.querySelectorAll('[data-vendor-letter-hover-lift="true"]'));
|
|
52
|
+
if (chips.length === 0) return null;
|
|
53
|
+
return {
|
|
54
|
+
count: chips.length,
|
|
55
|
+
sample: {
|
|
56
|
+
className: chips[0].className,
|
|
57
|
+
letter: chips[0].getAttribute('data-vendor-letter'),
|
|
58
|
+
vendorCount: chips[0].getAttribute('data-vendor-letter-count'),
|
|
59
|
+
lift: chips[0].getAttribute('data-vendor-letter-hover-lift'),
|
|
60
|
+
pinned: chips[0].getAttribute('data-vendor-pinned'),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await browser.close();
|
|
66
|
+
|
|
67
|
+
const cls = probe?.sample?.className || '';
|
|
68
|
+
const results = {
|
|
69
|
+
// At least one vendor chip rendered (3 vendors in fixture)
|
|
70
|
+
chips_count_ge_1: (probe?.count || 0) >= 1,
|
|
71
|
+
// R401 lift attrs + className tokens
|
|
72
|
+
lift_true: probe?.sample?.lift === 'true',
|
|
73
|
+
has_hover_translate: cls.includes('hover:-translate-y-px'),
|
|
74
|
+
has_transition_transform: cls.includes('transition-transform'),
|
|
75
|
+
has_transform_gpu: cls.includes('transform-gpu'),
|
|
76
|
+
// Pre-R401 invariants (R314 + R232 + R266)
|
|
77
|
+
has_tabular_nums: cls.includes('tabular-nums'),
|
|
78
|
+
has_font_medium: cls.includes('font-medium'),
|
|
79
|
+
has_chip_focus: cls.includes('anet-topo-chip-focus'),
|
|
80
|
+
has_inline_flex: cls.includes('inline-flex'),
|
|
81
|
+
has_letter_attr: !!probe?.sample?.letter,
|
|
82
|
+
has_count_attr: !!probe?.sample?.vendorCount,
|
|
83
|
+
};
|
|
84
|
+
const ok = Object.values(results).every(Boolean);
|
|
85
|
+
console.log(`${ok ? '✅' : '❌'} vendor letter chip hover lift translateY(-1px):`, JSON.stringify(results),
|
|
86
|
+
'\n chips:', probe?.count, '/ sample:', JSON.stringify(probe?.sample, null, 2));
|
|
87
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* Round 369 verification: vendor letter glyph fontWeight 600. Pre-
|
|
2
|
+
* R369 the glyph inherited fw 500 from the chip's font-medium —
|
|
3
|
+
* letter and `:N` count read at the same weight, contradicting the
|
|
4
|
+
* data-vs-label hierarchy the rest of the chip-row speaks (R362).
|
|
5
|
+
* R369 lifts the LETTER to fw 600 so the chip reads as the same
|
|
6
|
+
* two-tier pattern:
|
|
7
|
+
* letter (data) fw 600 (R369, this round)
|
|
8
|
+
* :N count fw 500 (R333 + chip-level font-medium)
|
|
9
|
+
*
|
|
10
|
+
* Extends the R333-R341/R362 chip-internal-hierarchy arc to the
|
|
11
|
+
* vendor-letter chip surface (9th surface family).
|
|
12
|
+
*
|
|
13
|
+
* Contract (cyber, fixture with 3 distinct vendors so the chip mounts):
|
|
14
|
+
* - Each rendered [data-vendor-letter-glyph] computed fontWeight === '600'.
|
|
15
|
+
* - data-vendor-letter-glyph-font-weight === '600'.
|
|
16
|
+
* - R354 transform-scale rest (matrix identity / 'none') preserved.
|
|
17
|
+
* - R354 transition list contains transform.
|
|
18
|
+
* - The sibling count suffix [data-vendor-letter-count-suffix]
|
|
19
|
+
* still has fontWeight inherited from chip's font-medium (500)
|
|
20
|
+
* — not bumped, so hierarchy is preserved.
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, model) => ({
|
|
38
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
// R281 vendor-dist threshold > 2 — provide 3 distinct vendors.
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('a1', 'claude-opus-4'),
|
|
45
|
+
mk('a2', 'claude-opus-4'),
|
|
46
|
+
mk('b1', 'gpt-4o'),
|
|
47
|
+
mk('b2', 'gpt-4o'),
|
|
48
|
+
mk('c1', 'internlm/internlm2'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForSelector('[data-vendor-letter-glyph]', { timeout: 15000 });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
const probe = await page.evaluate(() => {
|
|
60
|
+
const glyphs = Array.from(document.querySelectorAll('[data-vendor-letter-glyph]'));
|
|
61
|
+
return glyphs.map(el => {
|
|
62
|
+
const cs = getComputedStyle(el);
|
|
63
|
+
const chip = el.closest('[data-vendor-letter]');
|
|
64
|
+
const suffix = chip?.querySelector('[data-vendor-letter-count-suffix]');
|
|
65
|
+
const sCs = suffix ? getComputedStyle(suffix) : null;
|
|
66
|
+
return {
|
|
67
|
+
key: el.getAttribute('data-vendor-letter-glyph'),
|
|
68
|
+
fontWeight: cs.fontWeight,
|
|
69
|
+
fontWeightData: el.getAttribute('data-vendor-letter-glyph-font-weight'),
|
|
70
|
+
transform: cs.transform,
|
|
71
|
+
transition: cs.transition,
|
|
72
|
+
suffixFw: sCs?.fontWeight ?? null,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
const isRestTransform = (t) => t === 'none' || /matrix\(1, 0, 0, 1, 0, 0\)/.test(t || '');
|
|
80
|
+
const hasTrans = (s, prop) =>
|
|
81
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
82
|
+
|
|
83
|
+
const allOk = probe.length >= 3 && probe.every(g =>
|
|
84
|
+
g.fontWeight === '600'
|
|
85
|
+
&& g.fontWeightData === '600'
|
|
86
|
+
&& isRestTransform(g.transform)
|
|
87
|
+
&& hasTrans(g.transition, 'transform')
|
|
88
|
+
&& g.suffixFw === '500' // chip-level font-medium inheritance
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
glyphs_rendered_3plus: probe.length >= 3,
|
|
93
|
+
all_glyphs_fw_600: probe.every(g => g.fontWeight === '600'),
|
|
94
|
+
all_glyphs_data_600: probe.every(g => g.fontWeightData === '600'),
|
|
95
|
+
all_glyphs_rest_xform: probe.every(g => isRestTransform(g.transform)),
|
|
96
|
+
all_glyphs_trans_xform: probe.every(g => hasTrans(g.transition, 'transform')),
|
|
97
|
+
all_suffixes_fw_500: probe.every(g => g.suffixFw === '500'),
|
|
98
|
+
};
|
|
99
|
+
const ok = allOk && Object.values(results).every(Boolean);
|
|
100
|
+
console.log(`${ok ? '✅' : '❌'} vendor letter glyph fw 500 → 600:`, JSON.stringify(results),
|
|
101
|
+
'\n glyphs:', probe.map(g => ({ key: g.key, fw: g.fontWeight, suffixFw: g.suffixFw })));
|
|
102
|
+
process.exit(ok ? 0 : 1);
|