@sleep2agi/agent-network-dashboard 0.5.1-preview.5 → 0.5.1-preview.50
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/0arbuyjlvs835.js +1 -0
- package/.next/static/chunks/0hwb_h953qm4d.css +2 -0
- package/.next/static/chunks/0ivrr8m-3h4h7.js +1 -0
- package/.next/static/chunks/10d4r77zdl_td.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +960 -64
- package/package.json +1 -1
- package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
- package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
- package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
- package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
- package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -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-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-hub-digit-fontsize-test.mjs +86 -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-minimap-offline-opacity-test.mjs +90 -0
- package/scripts/topo-minimap-online-radius-test.mjs +85 -0
- package/scripts/topo-minimap-viewport-hover-test.mjs +109 -0
- package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
- package/scripts/topo-more-flows-fontweight-test.mjs +103 -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-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-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/020yd2d3i1yew.js +0 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/0txa4xkx0-7v-.js +0 -1
- package/.next/static/chunks/0xvj-x25qdu55.js +0 -4
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → 9QBAyvu6guSR1QZpVdp6x}/_buildManifest.js +0 -0
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → 9QBAyvu6guSR1QZpVdp6x}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → 9QBAyvu6guSR1QZpVdp6x}/_ssgManifest.js +0 -0
|
@@ -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,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);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Round 354 verification: vendor letter glyph scales 1.0 → 1.1 on
|
|
2
|
+
* hover. R88 already dims OTHER vendors via canvas-wide opacity
|
|
3
|
+
* masking; R202 added a chip-level bg tint (color-mix 12 % alpha) so
|
|
4
|
+
* the chip itself responds. R354 closes the trio with a glyph-level
|
|
5
|
+
* lift: the focused vendor LETTER actively rises (transform scale)
|
|
6
|
+
* rather than the chip merely changing colour.
|
|
7
|
+
*
|
|
8
|
+
* Three layers of positive feedback on the hovered vendor + canvas-
|
|
9
|
+
* wide negative feedback on the others — a clean figure/ground
|
|
10
|
+
* separation.
|
|
11
|
+
*
|
|
12
|
+
* Contract (cyber, fixture with 3 distinct vendors so R281's > 2
|
|
13
|
+
* threshold mounts the chip):
|
|
14
|
+
* - Rest: vendor letter glyph transform === matrix(1, 0, 0, 1, 0, 0)
|
|
15
|
+
* (identity).
|
|
16
|
+
* - Hover one vendor chip: that glyph transform === matrix(1.1, 0,
|
|
17
|
+
* 0, 1.1, 0, 0).
|
|
18
|
+
* - data-vendor-letter-glyph-hover toggles false → true on
|
|
19
|
+
* hovered chip only; sibling glyphs stay 'false'.
|
|
20
|
+
* - Inline transition contains 'transform'.
|
|
21
|
+
* - Pre-R354 invariants: R202 bg color-mix on hover still applies,
|
|
22
|
+
* data-vendor-letter-count-suffix span (R333) still present.
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
// R281 vendor-dist threshold is > 2 distinct vendors — provide 3.
|
|
40
|
+
const mk = (alias, model) => ({
|
|
41
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('a1', 'claude-opus-4'),
|
|
47
|
+
mk('a2', 'claude-opus-4'),
|
|
48
|
+
mk('b1', 'gpt-4o'),
|
|
49
|
+
mk('b2', 'gpt-4o'),
|
|
50
|
+
mk('c1', 'internlm/internlm2'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
55
|
+
|
|
56
|
+
const page = await ctx.newPage();
|
|
57
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
58
|
+
await page.waitForSelector('[data-vendor-letter-glyph]', { timeout: 15000 });
|
|
59
|
+
await page.waitForTimeout(300);
|
|
60
|
+
|
|
61
|
+
// Read all vendor letters present.
|
|
62
|
+
const inventory = await page.evaluate(() => {
|
|
63
|
+
const glyphs = Array.from(document.querySelectorAll('[data-vendor-letter-glyph]'));
|
|
64
|
+
return glyphs.map(el => el.getAttribute('data-vendor-letter-glyph'));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Pick the first vendor letter for the hover test.
|
|
68
|
+
const target = inventory[0];
|
|
69
|
+
|
|
70
|
+
const restProbe = await page.evaluate((tgt) => {
|
|
71
|
+
const glyph = document.querySelector(`[data-vendor-letter-glyph="${tgt}"]`);
|
|
72
|
+
const cs = glyph ? getComputedStyle(glyph) : null;
|
|
73
|
+
const chip = glyph ? glyph.closest('[data-vendor-letter]') : null;
|
|
74
|
+
const suffix = chip ? chip.querySelector('[data-vendor-letter-count-suffix]') : null;
|
|
75
|
+
return {
|
|
76
|
+
transform: cs?.transform ?? null,
|
|
77
|
+
transition: cs?.transition ?? null,
|
|
78
|
+
hoverAttr: glyph?.getAttribute('data-vendor-letter-glyph-hover') ?? null,
|
|
79
|
+
display: cs?.display ?? null,
|
|
80
|
+
suffixPresent: !!suffix,
|
|
81
|
+
};
|
|
82
|
+
}, target);
|
|
83
|
+
|
|
84
|
+
// Hover the target vendor letter's chip.
|
|
85
|
+
await page.hover(`[data-vendor-letter="${target}"]`);
|
|
86
|
+
await page.waitForTimeout(300);
|
|
87
|
+
const hoverProbe = await page.evaluate((tgt) => {
|
|
88
|
+
const glyph = document.querySelector(`[data-vendor-letter-glyph="${tgt}"]`);
|
|
89
|
+
const cs = glyph ? getComputedStyle(glyph) : null;
|
|
90
|
+
// Read all sibling hover attrs to confirm only one is active.
|
|
91
|
+
const all = Array.from(document.querySelectorAll('[data-vendor-letter-glyph]'))
|
|
92
|
+
.map(el => ({ key: el.getAttribute('data-vendor-letter-glyph'),
|
|
93
|
+
hover: el.getAttribute('data-vendor-letter-glyph-hover') }));
|
|
94
|
+
return {
|
|
95
|
+
transform: cs?.transform ?? null,
|
|
96
|
+
hoverAttr: glyph?.getAttribute('data-vendor-letter-glyph-hover') ?? null,
|
|
97
|
+
siblings: all,
|
|
98
|
+
};
|
|
99
|
+
}, target);
|
|
100
|
+
|
|
101
|
+
await browser.close();
|
|
102
|
+
|
|
103
|
+
const isIdentity = (t) => t === 'none' || /matrix\(1, 0, 0, 1, 0, 0\)/.test(t || '');
|
|
104
|
+
const isScale110 = (t) => /matrix\(1\.1, 0, 0, 1\.1, 0, 0\)/.test(t || '');
|
|
105
|
+
const hasTrans = (s, prop) =>
|
|
106
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
107
|
+
|
|
108
|
+
const siblingsOnlyOneTrue = hoverProbe.siblings.filter(s => s.hover === 'true').length === 1
|
|
109
|
+
&& hoverProbe.siblings.find(s => s.key === target)?.hover === 'true';
|
|
110
|
+
|
|
111
|
+
const results = {
|
|
112
|
+
inventory_has_glyphs: inventory.length >= 3,
|
|
113
|
+
rest_identity: isIdentity(restProbe.transform),
|
|
114
|
+
rest_hover_false: restProbe.hoverAttr === 'false',
|
|
115
|
+
// Flex children get display: block computed (spec blockification),
|
|
116
|
+
// even when declared inline-block — accept either for safety.
|
|
117
|
+
rest_display_block: restProbe.display === 'inline-block' || restProbe.display === 'block',
|
|
118
|
+
trans_has_transform: hasTrans(restProbe.transition, 'transform'),
|
|
119
|
+
suffix_present: restProbe.suffixPresent, // R333 preserved
|
|
120
|
+
hover_scale_110: isScale110(hoverProbe.transform),
|
|
121
|
+
hover_attr_true: hoverProbe.hoverAttr === 'true',
|
|
122
|
+
hover_isolates_target: siblingsOnlyOneTrue,
|
|
123
|
+
};
|
|
124
|
+
const ok = Object.values(results).every(Boolean);
|
|
125
|
+
console.log(`${ok ? '✅' : '❌'} vendor letter glyph hover-scale:`, JSON.stringify(results),
|
|
126
|
+
'\n inventory:', inventory,
|
|
127
|
+
'\n rest: ', restProbe,
|
|
128
|
+
'\n hover: ', hoverProbe);
|
|
129
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* Round 352 verification: zoom-out + zoom-in icons gain group-hover:
|
|
2
|
+
* scale-110 — sibling to R350 reset hover-rotate. Pre-R352 hovering
|
|
3
|
+
* the zoom buttons only changed the bg (white/5); the icons stayed
|
|
4
|
+
* still. R352 lifts each icon 10 % on hover for a tactile "this
|
|
5
|
+
* button does something" cue. Pure-CSS via Tailwind `group` parent +
|
|
6
|
+
* `group-hover:scale-110` on the svg + `transition-transform
|
|
7
|
+
* duration-200 ease-out`. CSS-animation precedence keeps R186
|
|
8
|
+
* anet-chrome-pop owning transform during the 220 ms click pop;
|
|
9
|
+
* group-hover scale-110 picks up smoothly afterward.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Rest: both zoom icons computed transform 'matrix(1, 0, 0, 1, 0, 0)'
|
|
13
|
+
* (or 'none').
|
|
14
|
+
* - Hover zoom-out button: zoom-out icon transform matrix(1.1, 0, 0,
|
|
15
|
+
* 1.1, 0, 0).
|
|
16
|
+
* - Hover zoom-in button: zoom-in icon transform matrix(1.1, 0, 0,
|
|
17
|
+
* 1.1, 0, 0).
|
|
18
|
+
* - Inline transition on both icons contains 'transform'.
|
|
19
|
+
* - Pre-R352 invariants: data-topo-chrome-zoom-{out,in}-icon attrs
|
|
20
|
+
* intact, R288 strokeWidth=2.5 preserved, anet-chrome-pop class
|
|
21
|
+
* absent at rest.
|
|
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-zoom-out-icon]', { timeout: 15000 });
|
|
51
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in-icon]', { timeout: 5000 });
|
|
52
|
+
await page.waitForTimeout(300);
|
|
53
|
+
|
|
54
|
+
// Tailwind 4 compiles scale-110 to the modern CSS `scale` property
|
|
55
|
+
// (not legacy `transform: scale()`), so we probe both — `transform`
|
|
56
|
+
// stays at identity matrix and `scale` carries the 1.1 value.
|
|
57
|
+
const restProbe = await page.evaluate(() => {
|
|
58
|
+
const oico = document.querySelector('[data-topo-chrome-zoom-out-icon]');
|
|
59
|
+
const iico = document.querySelector('[data-topo-chrome-zoom-in-icon]');
|
|
60
|
+
const ocs = oico ? getComputedStyle(oico) : null;
|
|
61
|
+
const ics = iico ? getComputedStyle(iico) : null;
|
|
62
|
+
return {
|
|
63
|
+
outScale: ocs?.scale ?? null,
|
|
64
|
+
outTransition: ocs?.transition ?? null,
|
|
65
|
+
outStrokeW: oico?.getAttribute('stroke-width') ?? null,
|
|
66
|
+
outClass: oico?.getAttribute('class') ?? null,
|
|
67
|
+
inScale: ics?.scale ?? null,
|
|
68
|
+
inTransition: ics?.transition ?? null,
|
|
69
|
+
inStrokeW: iico?.getAttribute('stroke-width') ?? null,
|
|
70
|
+
inClass: iico?.getAttribute('class') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Hover zoom-out button.
|
|
75
|
+
await page.hover('[data-topo-chrome-zoom-out]');
|
|
76
|
+
await page.waitForTimeout(300);
|
|
77
|
+
const zoomOutHover = await page.evaluate(() => {
|
|
78
|
+
const ico = document.querySelector('[data-topo-chrome-zoom-out-icon]');
|
|
79
|
+
return { scale: ico ? getComputedStyle(ico).scale : null };
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Hover zoom-in button.
|
|
83
|
+
await page.hover('[data-topo-chrome-zoom-in]');
|
|
84
|
+
await page.waitForTimeout(300);
|
|
85
|
+
const zoomInHover = await page.evaluate(() => {
|
|
86
|
+
const ico = document.querySelector('[data-topo-chrome-zoom-in-icon]');
|
|
87
|
+
return { scale: ico ? getComputedStyle(ico).scale : null };
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const isRestScale = (s) => s === 'none' || s === '1' || s === '1 1' || s === null;
|
|
93
|
+
const isHoverScale110 = (s) => s === '1.1' || s === '1.1 1.1';
|
|
94
|
+
const hasTrans = (s, prop) =>
|
|
95
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
96
|
+
|
|
97
|
+
const results = {
|
|
98
|
+
rest_out_scale_1: isRestScale(restProbe.outScale),
|
|
99
|
+
rest_in_scale_1: isRestScale(restProbe.inScale),
|
|
100
|
+
out_trans_has_scale: hasTrans(restProbe.outTransition, 'scale') || hasTrans(restProbe.outTransition, 'transform'),
|
|
101
|
+
in_trans_has_scale: hasTrans(restProbe.inTransition, 'scale') || hasTrans(restProbe.inTransition, 'transform'),
|
|
102
|
+
out_strokew_2_5: restProbe.outStrokeW === '2.5',
|
|
103
|
+
in_strokew_2_5: restProbe.inStrokeW === '2.5',
|
|
104
|
+
out_no_pop: !/anet-chrome-pop/.test(restProbe.outClass || ''),
|
|
105
|
+
in_no_pop: !/anet-chrome-pop/.test(restProbe.inClass || ''),
|
|
106
|
+
hover_out_scale_110: isHoverScale110(zoomOutHover.scale),
|
|
107
|
+
hover_in_scale_110: isHoverScale110(zoomInHover.scale),
|
|
108
|
+
};
|
|
109
|
+
const ok = Object.values(results).every(Boolean);
|
|
110
|
+
console.log(`${ok ? '✅' : '❌'} zoom icons hover-scale:`, JSON.stringify(results),
|
|
111
|
+
'\n rest: ', restProbe,
|
|
112
|
+
'\n hover zoom-out: ', zoomOutHover,
|
|
113
|
+
'\n hover zoom-in: ', zoomInHover);
|
|
114
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* Round 347 verification: zoom-level readout in the chrome strip
|
|
2
|
+
* gains a hover-state letter-spacing tween 0 → 0.5 px. Extends the
|
|
3
|
+
* R344/R345 hover-letter-spacing family from panel/footer surfaces
|
|
4
|
+
* into the HTML chrome strip. The readout is sandwiched between
|
|
5
|
+
* zoom-out / zoom-in buttons; hovering it spreads digits 0.5 px,
|
|
6
|
+
* signalling "this is alive". tabular-nums + minWidth: 46 from R225
|
|
7
|
+
* lock the column so the tween doesn't shove neighbouring controls.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - Rest: zoom-level letter-spacing 0 (or 'normal' / '0px').
|
|
11
|
+
* - Hover: letter-spacing 0.5px.
|
|
12
|
+
* - data-topo-chrome-zoom-level-hover toggles false → true.
|
|
13
|
+
* - transition list contains 'letter-spacing'.
|
|
14
|
+
* - Pre-R347 invariants intact: R264 color + border-color transitions
|
|
15
|
+
* preserved; tabular-nums + minWidth=46 still applied.
|
|
16
|
+
*/
|
|
17
|
+
import { chromium } from 'playwright';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
|
|
20
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
21
|
+
const 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
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias) => ({
|
|
33
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
36
|
+
});
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
38
|
+
});
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
40
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
41
|
+
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForSelector('[data-topo-chrome-zoom-level]', { timeout: 15000 });
|
|
45
|
+
await page.waitForTimeout(400);
|
|
46
|
+
|
|
47
|
+
const restProbe = await page.evaluate(() => {
|
|
48
|
+
const el = document.querySelector('[data-topo-chrome-zoom-level]');
|
|
49
|
+
const cs = el ? getComputedStyle(el) : null;
|
|
50
|
+
return {
|
|
51
|
+
ls: cs?.letterSpacing ?? null,
|
|
52
|
+
transition: cs?.transition ?? null,
|
|
53
|
+
hoverAttr: el?.getAttribute('data-topo-chrome-zoom-level-hover') ?? null,
|
|
54
|
+
minWidth: cs?.minWidth ?? null,
|
|
55
|
+
fontVariant: cs?.fontVariantNumeric ?? null,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
await page.hover('[data-topo-chrome-zoom-level]');
|
|
60
|
+
await page.waitForTimeout(250);
|
|
61
|
+
const hoverProbe = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-topo-chrome-zoom-level]');
|
|
63
|
+
const cs = el ? getComputedStyle(el) : null;
|
|
64
|
+
return {
|
|
65
|
+
ls: cs?.letterSpacing ?? null,
|
|
66
|
+
hoverAttr: el?.getAttribute('data-topo-chrome-zoom-level-hover') ?? null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await browser.close();
|
|
71
|
+
|
|
72
|
+
const hasTrans = (s, prop) =>
|
|
73
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
74
|
+
const isRestLs = (v) => v === 'normal' || v === '0px' || v === '0' || v === null;
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
rest_ls_zero: isRestLs(restProbe.ls),
|
|
78
|
+
rest_hover_attr_false: restProbe.hoverAttr === 'false',
|
|
79
|
+
trans_has_letterspacing: hasTrans(restProbe.transition, 'letter-spacing'),
|
|
80
|
+
trans_has_color: hasTrans(restProbe.transition, 'color'), // R264 preserved
|
|
81
|
+
trans_has_border: hasTrans(restProbe.transition, 'border-color'),
|
|
82
|
+
min_width_46: restProbe.minWidth === '46px',
|
|
83
|
+
tabular_nums: /tabular-nums/.test(restProbe.fontVariant || ''),
|
|
84
|
+
hover_ls_0_5: hoverProbe.ls === '0.5px',
|
|
85
|
+
hover_attr_true: hoverProbe.hoverAttr === 'true',
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} zoom level hover letter-spacing:`, JSON.stringify(results),
|
|
89
|
+
'\n rest: ', restProbe,
|
|
90
|
+
'\n hover:', hoverProbe);
|
|
91
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,59276,e=>{"use strict";var s=e.i(12629),a=e.i(11449);let t=async e=>{let s=await fetch(e);if(401===s.status)throw window.location.assign("/login"),Error("unauthorized");return s.json()},r={refreshInterval:5e3,dedupingInterval:3e3};function n(e,s){if(!s)return e;let a=e.includes("?")?"&":"?";return`${e}${a}network_id=${encodeURIComponent(s)}`}e.s(["useAnetConfig",0,function(){let{data:e}=(0,s.default)("/api/anet/config",t,{refreshInterval:3e4});return{config:e||null}},"useHealth",0,function(){let{data:e,error:a}=(0,s.default)("/api/hub/health",t,r);return{health:e||null,error:a}},"useLicense",0,function(){let{data:e,error:a}=(0,s.default)("/api/hub/license",t,{refreshInterval:6e4});return{license:e?.ok?e:null,error:a}},"useMessages",0,function(e=100){let{networkId:l}=(0,a.useNetworkId)(),{data:i,error:c,isLoading:d}=(0,s.default)(n(`/api/hub/messages?limit=${e}`,l),t,r);return{messages:i?.messages||[],error:c,isLoading:d}},"useSessions",0,function(){let{networkId:e}=(0,a.useNetworkId)(),{data:l,error:i,isLoading:c}=(0,s.default)(n("/api/hub/status",e),t,r);return{sessions:l?.sessions||[],hint:l?._hint,error:i,isLoading:c}},"useStats",0,function(){let{networkId:e}=(0,a.useNetworkId)(),{data:l,error:i}=(0,s.default)(n("/api/hub/stats",e),t,r);return{stats:l?.ok?l:null,error:i}},"useTasks",0,function(e){let{networkId:l}=(0,a.useNetworkId)(),i=new URLSearchParams({limit:"100",...e}).toString(),{data:c,error:d,isLoading:o}=(0,s.default)(n(`/api/hub/tasks?${i}`,l),t,r);return{tasks:c?.tasks||[],count:c?.count??0,source:c?.source,error:d,isLoading:o}}])},70286,e=>{"use strict";e.s(["DASHBOARD_VERSION",0,"0.5.1-preview.5"],70286)},65610,e=>{"use strict";var s=e.i(22381),a=e.i(23910),t=e.i(56839),r=e.i(59276),n=e.i(70286);e.s(["default",0,function(){let{config:e}=(0,r.useAnetConfig)(),{health:l}=(0,r.useHealth)(),{license:i}=(0,r.useLicense)(),[c,d]=(0,a.useState)(""),[o,x]=(0,a.useState)(""),[m,h]=(0,a.useState)(""),[g,u]=(0,a.useState)(""),[p,N]=(0,a.useState)(""),b="flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between",j="break-all sm:max-w-[320px] sm:text-right";return(0,s.jsxs)("div",{className:"min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono",children:[(0,s.jsx)("h1",{className:"text-2xl font-bold text-white mb-3 lg:ml-0 ml-10",children:"Settings"}),(0,s.jsx)("nav",{className:"mb-8 flex flex-wrap gap-1 text-xs",children:[{href:"#connection",label:"Connection"},{href:"#account",label:"Account"},{href:"#resources",label:"Resources"}].map(e=>(0,s.jsx)("a",{href:e.href,className:"rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors",children:e.label},e.href))}),(0,s.jsxs)("div",{className:"max-w-2xl space-y-10",children:[(0,s.jsxs)("div",{id:"connection",className:"space-y-4 scroll-mt-6",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2 px-1",children:[(0,s.jsx)("div",{className:"text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold",children:"Connection"}),(0,s.jsx)("div",{className:"flex-1 h-px bg-[#2a2a4a]"})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-4",children:"CommHub Connection"}),(0,s.jsxs)("div",{className:"space-y-3 text-sm",children:[(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500 shrink-0",children:"Hub URL"}),(0,s.jsx)("span",{className:`text-cyan-300 ${j}`,children:e?.hub||"not configured"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Config Source"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:e?.source==="file"?"Local file (~/.anet/config.json)":e?.source==="runtime-env"?"Vercel env vars":"Missing"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Auth Token"}),(0,s.jsx)("span",{className:`${e?.tokenConfigured?"text-green-400":"text-red-400"} ${j}`,children:e?.tokenConfigured?`Configured (${e.tokenPreview})`:"Not configured"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Server Auth"}),(0,s.jsx)("span",{className:`${l?.auth==="enabled"?"text-green-400":"text-yellow-400"} sm:text-right`,children:l?.auth||"--"})]}),e?.error&&(0,s.jsx)("div",{className:"border-t border-[#2a2a4a] pt-3 text-xs text-gray-600",children:e.error})]})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-4",children:"Server Info"}),(0,s.jsxs)("div",{className:"space-y-3 text-sm",children:[(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Version"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:l?.version||"--"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Sessions"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:l?.sessions??"--"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsxs)("span",{className:"text-gray-500",children:["SSE streams ",(0,s.jsx)("span",{className:"text-gray-600",children:"· server"})]}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:l?.sse_connections??"--"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Uptime"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:l?.uptime?`${Math.floor(l.uptime/3600)}h ${Math.floor(l.uptime%3600/60)}m`:"--"})]})]})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-4",children:"Dashboard"}),(0,s.jsxs)("div",{className:"space-y-3 text-sm",children:[(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Version"}),(0,s.jsx)("span",{className:`text-gray-300 font-mono ${j}`,title:"From package.json @sleep2agi/agent-network-dashboard",children:n.DASHBOARD_VERSION})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Data Layer"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:"SWR (5s refresh, 3s dedup)"})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Pages"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:"Overview, Tasks, Nodes, Messages, Networks, Audit, Server Logs, Admin, Settings"})]})]})]})]}),(0,s.jsxs)("div",{id:"account",className:"space-y-4 scroll-mt-6",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2 px-1",children:[(0,s.jsx)("div",{className:"text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold",children:"Account"}),(0,s.jsx)("div",{className:"flex-1 h-px bg-[#2a2a4a]"})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsxs)("h2",{className:"text-sm font-semibold text-gray-300 mb-4 flex items-center gap-2",children:["License",i?.license&&(0,s.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-[10px] font-medium px-2 py-0.5 rounded-full border ${"pro"===i.license.type?"text-green-300 bg-green-500/10 border-green-500/30":i.license.days_left<=7?"text-red-300 bg-red-500/10 border-red-500/30":"text-amber-300 bg-amber-500/10 border-amber-500/30"}`,children:[(0,s.jsx)("span",{"aria-hidden":!0,className:"w-1.5 h-1.5 rounded-full bg-current"}),i.license.type,i.license.days_left?` \xb7 ${i.license.days_left}d left`:""]})]}),i?.license?(0,s.jsxs)("div",{className:"space-y-3 text-sm",children:[i.license.days_left<=7&&(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-red-400 font-medium",children:"⚠ Expiring soon"}),(0,s.jsxs)("span",{className:"text-red-400",children:[i.license.days_left," days left"]})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Expires"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:i.license.expires_at})]}),i.limits&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Max Agents"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:i.limits.max_agents})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Max Networks"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:i.limits.max_networks})]}),(0,s.jsxs)("div",{className:b,children:[(0,s.jsx)("span",{className:"text-gray-500",children:"Tasks/Day"}),(0,s.jsx)("span",{className:`text-gray-300 ${j}`,children:i.limits.max_tasks_day})]})]}),(0,s.jsxs)("div",{className:"pt-3 border-t border-[#2a2a4a]",children:[(0,s.jsxs)("div",{className:"flex gap-2",children:[(0,s.jsx)("input",{type:"text",value:c,onChange:e=>d(e.target.value),placeholder:"anet-XXXX-XXXX-XXXX-XXXX",className:"flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-xs text-white placeholder-gray-600 focus:outline-none"}),(0,s.jsx)("button",{onClick:async()=>{if(!c.trim())return;let e=await fetch("/api/hub/license",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:c})}),s=await e.json();x(s.ok?`Activated: ${s.type}`:`Failed: ${s.error}`),s.ok&&d(""),setTimeout(()=>x(""),5e3)},className:"px-3 py-2 bg-cyan-600 hover:bg-cyan-500 text-white text-xs rounded transition-colors",children:"Activate"})]}),o&&(0,s.jsx)("div",{className:`mt-2 text-xs ${o.startsWith("Failed")?"text-red-400":"text-green-400"}`,children:o})]})]}):(0,s.jsx)("div",{className:"text-xs text-gray-600",children:"License info not available"})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-4",children:"Change Password"}),(0,s.jsxs)("div",{className:"space-y-3",children:[(0,s.jsx)("input",{type:"password",value:m,onChange:e=>h(e.target.value),placeholder:"Current password",className:"w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none"}),(0,s.jsx)("input",{type:"password",value:g,onChange:e=>u(e.target.value),placeholder:"New password",className:"w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none"}),(0,s.jsx)("button",{onClick:async()=>{if(!m||!g)return;let e=sessionStorage.getItem("anet_v3_auth");if(!e)return void N("Not logged in with V3 auth");let{token:s}=JSON.parse(e),a=await fetch("/api/hub/auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"change_password",token:s,current_password:m,new_password:g})}),t=await a.json();if(N(t.ok?"Password changed":`Failed: ${t.error}`),t.ok){if(t.token){let s=JSON.parse(e);sessionStorage.setItem("anet_v3_auth",JSON.stringify({...s,token:t.token}))}h(""),u("")}setTimeout(()=>N(""),5e3)},disabled:!m||!g,className:"px-4 py-2 bg-cyan-600 hover:bg-cyan-500 disabled:bg-gray-800 disabled:text-gray-600 text-white text-sm rounded-lg transition-colors",children:"Change Password"})]}),p&&(0,s.jsx)("div",{className:`mt-2 text-xs ${p.startsWith("Failed")?"text-red-400":"text-green-400"}`,children:p})]}),(0,s.jsxs)("section",{className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-4",children:"Sign out"}),(0,s.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Signing out clears your dashboard session cookie. You'll return to the login page."}),(0,s.jsx)("button",{onClick:async()=>{await fetch("/api/auth/logout",{method:"POST"}).catch(()=>{}),window.location.assign("/login")},className:"px-4 py-2 bg-transparent hover:bg-[#1a1a2a] text-gray-300 text-sm rounded-lg border border-[#2a2a4a] hover:border-[#3a3a5a] transition-colors",children:"Sign out"})]})]}),(0,s.jsxs)("div",{id:"resources",className:"space-y-4 scroll-mt-6",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2 px-1",children:[(0,s.jsx)("div",{className:"text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold",children:"Resources"}),(0,s.jsx)("div",{className:"flex-1 h-px bg-[#2a2a4a]"})]}),(0,s.jsxs)("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[(0,s.jsxs)(t.default,{href:"/settings/tokens",className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300",children:"API Tokens"}),(0,s.jsx)("p",{className:"text-xs text-gray-500 mt-2",children:"Create and manage tokens for CLI access."}),(0,s.jsx)("span",{className:"text-xs text-cyan-400 mt-3 inline-block",children:"Manage →"})]}),(0,s.jsxs)(t.default,{href:"/settings/networks",className:"bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-gray-300",children:"Networks"}),(0,s.jsx)("p",{className:"text-xs text-gray-500 mt-2",children:"Create, manage, and delete agent networks."}),(0,s.jsx)("span",{className:"text-xs text-cyan-400 mt-3 inline-block",children:"Manage →"})]})]})]})]})]})}])}]);
|