@sleep2agi/agent-network-dashboard 0.5.1-preview.4 → 0.5.1-preview.40
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/05.asnoxq2t39.js +1 -0
- package/.next/static/chunks/06_k1dgr3isa2.js +1 -0
- package/.next/static/chunks/0fxhdt5-198se.js +4 -0
- package/.next/static/chunks/0hwb_h953qm4d.css +2 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +760 -58
- 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-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-label-count-fontweight-test.mjs +108 -0
- package/scripts/topo-hub-digit-fontsize-test.mjs +86 -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-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-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-more-flows-hover-letterspacing-test.mjs +98 -0
- package/scripts/topo-panel-count-letterspacing-test.mjs +89 -0
- package/scripts/topo-panel-rect-opacity-hover-test.mjs +109 -0
- package/scripts/topo-panel-title-hover-letterspacing-test.mjs +97 -0
- package/scripts/topo-pressure-bar-height-test.mjs +92 -0
- package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -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/04sya_8oheege.js +0 -4
- package/.next/static/chunks/08c-dt44c.swe.js +0 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/12e0z26h2p0xl.js +0 -1
- /package/.next/static/{CkuUcpLXVJ-OsmjZqi3xX → PtUnxSzwOvGhpWPUawz9k}/_buildManifest.js +0 -0
- /package/.next/static/{CkuUcpLXVJ-OsmjZqi3xX → PtUnxSzwOvGhpWPUawz9k}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{CkuUcpLXVJ-OsmjZqi3xX → PtUnxSzwOvGhpWPUawz9k}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 362 verification: 3 chip-row chip digits (working / online /
|
|
2
|
+
* active-links) wrap their digit in a font-semibold span, lifting it
|
|
3
|
+
* fw 500 (chip-row R313 baseline) → fw 600 within each chip. The
|
|
4
|
+
* outer chip className keeps font-medium so the chip's overall data-
|
|
5
|
+
* weight stays consistent with the R313 family; the digit override
|
|
6
|
+
* adds a within-chip tier:
|
|
7
|
+
* digit fw 600 + full opacity
|
|
8
|
+
* unit fw 500 + R338 opacity-70
|
|
9
|
+
*
|
|
10
|
+
* Joins the R333-R341 chip-internal-hierarchy arc at the chip-count
|
|
11
|
+
* scope (8th surface family).
|
|
12
|
+
*
|
|
13
|
+
* Contract (cyber, fresh fixture):
|
|
14
|
+
* - Each of the 3 chip digit spans has data-{name}-chip-digit attr.
|
|
15
|
+
* - Computed fontWeight of the digit span === '600'.
|
|
16
|
+
* - The unit sibling span still has opacity 0.7 + data-{name}-chip-
|
|
17
|
+
* unit attr (R338 invariants).
|
|
18
|
+
* - Parent chip retains font-medium (R313 baseline).
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
41
|
+
});
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
44
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
45
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
46
|
+
] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForSelector('[data-working-chip-digit]', { timeout: 15000 });
|
|
52
|
+
await page.waitForSelector('[data-online-chip-digit]', { timeout: 5000 });
|
|
53
|
+
await page.waitForSelector('[data-active-links-chip-digit]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
function readChip(digitSel, unitSel, parentSel) {
|
|
58
|
+
const digit = document.querySelector(digitSel);
|
|
59
|
+
const unit = document.querySelector(unitSel);
|
|
60
|
+
const parent = document.querySelector(parentSel);
|
|
61
|
+
return {
|
|
62
|
+
digitFw: digit ? getComputedStyle(digit).fontWeight : null,
|
|
63
|
+
digitText: digit?.textContent ?? null,
|
|
64
|
+
unitOp: unit ? getComputedStyle(unit).opacity : null,
|
|
65
|
+
unitPresent: !!unit,
|
|
66
|
+
parentCls: parent?.getAttribute('class') ?? null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
working: readChip('[data-working-chip-digit]', '[data-working-chip-unit]', '[data-working-chip]'),
|
|
71
|
+
online: readChip('[data-online-chip-digit]', '[data-online-chip-unit]', '[data-online-chip]'),
|
|
72
|
+
activeLinks: readChip('[data-active-links-chip-digit]', '[data-active-links-chip-unit]', '[data-active-links-chip]'),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await browser.close();
|
|
77
|
+
|
|
78
|
+
const isFw600 = (v) => v === '600' || v === 'bold'; // Tailwind font-semibold === 600
|
|
79
|
+
const approxEq = (a, b, tol = 0.02) => {
|
|
80
|
+
const x = parseFloat(a); const y = parseFloat(b);
|
|
81
|
+
return Number.isFinite(x) && Number.isFinite(y) && Math.abs(x - y) <= tol;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const checkChip = (chip) => ({
|
|
85
|
+
digit_fw_600: isFw600(chip.digitFw),
|
|
86
|
+
unit_op_0_7: approxEq(chip.unitOp, 0.7), // R338
|
|
87
|
+
unit_present: chip.unitPresent,
|
|
88
|
+
parent_medium: /font-medium/.test(chip.parentCls || ''), // R313
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
working_digit_fw_600: checkChip(probe.working).digit_fw_600,
|
|
93
|
+
working_unit_op_0_7: checkChip(probe.working).unit_op_0_7,
|
|
94
|
+
working_parent_medium: checkChip(probe.working).parent_medium,
|
|
95
|
+
online_digit_fw_600: checkChip(probe.online).digit_fw_600,
|
|
96
|
+
online_unit_op_0_7: checkChip(probe.online).unit_op_0_7,
|
|
97
|
+
online_parent_medium: checkChip(probe.online).parent_medium,
|
|
98
|
+
active_digit_fw_600: checkChip(probe.activeLinks).digit_fw_600,
|
|
99
|
+
active_unit_op_0_7: checkChip(probe.activeLinks).unit_op_0_7,
|
|
100
|
+
active_parent_medium: checkChip(probe.activeLinks).parent_medium,
|
|
101
|
+
};
|
|
102
|
+
const ok = Object.values(results).every(Boolean);
|
|
103
|
+
console.log(`${ok ? '✅' : '❌'} chip-row digit fontWeight 500 → 600:`, JSON.stringify(results),
|
|
104
|
+
'\n probe:', JSON.stringify(probe, null, 2));
|
|
105
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Round 376 verification: nodeSize + zoom segmented control wrappers
|
|
2
|
+
* rounded-md → rounded-lg (6 → 8 px). Sibling polish to R375 Layout-
|
|
3
|
+
* toggle wrapper — three chrome-strip segmented controls now all
|
|
4
|
+
* share rounded-lg at the wrapper tier:
|
|
5
|
+
* R375 Layout-toggle wrapper rounded-lg 8 px
|
|
6
|
+
* R376 nodeSize wrapper rounded-lg 8 px (this round)
|
|
7
|
+
* R376 zoom wrapper rounded-lg 8 px (this round)
|
|
8
|
+
*
|
|
9
|
+
* Individual atomic chrome buttons (reset, fullscreen) keep rounded-
|
|
10
|
+
* md (6 px) as their own atomic-button tier — chrome strip now
|
|
11
|
+
* expresses a two-tier hierarchy: 'segmented control container'
|
|
12
|
+
* (rounded-lg) vs 'standalone button' (rounded-md).
|
|
13
|
+
*
|
|
14
|
+
* Contract:
|
|
15
|
+
* - [data-topo-chrome-nodesize-radius] computed border-radius '8px'.
|
|
16
|
+
* - data-topo-chrome-nodesize-radius === 'rounded-lg'.
|
|
17
|
+
* - [data-topo-chrome-zoom-wrapper-radius] computed border-radius '8px'.
|
|
18
|
+
* - data-topo-chrome-zoom-wrapper-radius === 'rounded-lg'.
|
|
19
|
+
* - Pre-R376 invariants: border 1px + flex display + overflow:hidden
|
|
20
|
+
* + R263 background-color/border-color transition preserved on
|
|
21
|
+
* each wrapper.
|
|
22
|
+
* - Atomic reset + fullscreen buttons still have border-radius 6px
|
|
23
|
+
* (rounded-md, untouched).
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias) => ({
|
|
41
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForSelector('[data-topo-chrome-nodesize-radius]', { timeout: 15000 });
|
|
53
|
+
await page.waitForSelector('[data-topo-chrome-zoom-wrapper-radius]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const nodesize = document.querySelector('[data-topo-chrome-nodesize-radius]');
|
|
58
|
+
const zoom = document.querySelector('[data-topo-chrome-zoom-wrapper-radius]');
|
|
59
|
+
const reset = document.querySelector('[data-topo-chrome-reset]');
|
|
60
|
+
const fs = document.querySelector('[data-topo-chrome-fullscreen]');
|
|
61
|
+
const ncs = nodesize ? getComputedStyle(nodesize) : null;
|
|
62
|
+
const zcs = zoom ? getComputedStyle(zoom) : null;
|
|
63
|
+
const rcs = reset ? getComputedStyle(reset) : null;
|
|
64
|
+
const fcs = fs ? getComputedStyle(fs) : null;
|
|
65
|
+
return {
|
|
66
|
+
nodesizeRadius: ncs?.borderRadius ?? null,
|
|
67
|
+
nodesizeData: nodesize?.getAttribute('data-topo-chrome-nodesize-radius') ?? null,
|
|
68
|
+
nodesizeBorderW: ncs?.borderWidth ?? null,
|
|
69
|
+
nodesizeTrans: ncs?.transition ?? null,
|
|
70
|
+
zoomRadius: zcs?.borderRadius ?? null,
|
|
71
|
+
zoomData: zoom?.getAttribute('data-topo-chrome-zoom-wrapper-radius') ?? null,
|
|
72
|
+
zoomBorderW: zcs?.borderWidth ?? null,
|
|
73
|
+
zoomTrans: zcs?.transition ?? null,
|
|
74
|
+
resetRadius: rcs?.borderRadius ?? null,
|
|
75
|
+
fullscreenRadius: fcs?.borderRadius ?? null,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await browser.close();
|
|
80
|
+
|
|
81
|
+
const hasTrans = (s, prop) =>
|
|
82
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
nodesize_radius_8px: probe.nodesizeRadius === '8px', // rounded-lg
|
|
86
|
+
nodesize_data_lg: probe.nodesizeData === 'rounded-lg',
|
|
87
|
+
nodesize_border_1px: probe.nodesizeBorderW === '1px',
|
|
88
|
+
nodesize_trans_border: hasTrans(probe.nodesizeTrans, 'border-color'), // R263 / R268
|
|
89
|
+
zoom_radius_8px: probe.zoomRadius === '8px',
|
|
90
|
+
zoom_data_lg: probe.zoomData === 'rounded-lg',
|
|
91
|
+
zoom_border_1px: probe.zoomBorderW === '1px',
|
|
92
|
+
zoom_trans_border: hasTrans(probe.zoomTrans, 'border-color'),
|
|
93
|
+
// Atomic chrome buttons stay rounded-md (6 px) — invariant.
|
|
94
|
+
reset_still_md: probe.resetRadius === '6px',
|
|
95
|
+
fullscreen_still_md: probe.fullscreenRadius === '6px',
|
|
96
|
+
};
|
|
97
|
+
const ok = Object.values(results).every(Boolean);
|
|
98
|
+
console.log(`${ok ? '✅' : '❌'} chrome segmented wrappers rounded-md → rounded-lg:`, JSON.stringify(results),
|
|
99
|
+
'\n probe:', probe);
|
|
100
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 361 verification: edge midpoint badge text fontSize 10 → 11.
|
|
2
|
+
* Sibling visual-weight bump to R360 hub digit 11 → 12. The badge
|
|
3
|
+
* digit is the per-edge equivalent of the hub digit — a high-info
|
|
4
|
+
* scalar (link.count) at a stable canvas position. Pre-R361 the
|
|
5
|
+
* digit at fontSize=10 was readable but small against the r=9 /
|
|
6
|
+
* 18-px badge envelope; fontSize=11 nudges the glyph ~10 % bigger
|
|
7
|
+
* (bbox ~7×10 px from ~6×9 px). Still well inside the r=9 idle +
|
|
8
|
+
* r=10.5 hover/pin (R164) envelope.
|
|
9
|
+
*
|
|
10
|
+
* Contract:
|
|
11
|
+
* - Edge badge text font-size attr === '11'.
|
|
12
|
+
* - data-edge-badge-text-font-size === '11'.
|
|
13
|
+
* - Pre-R361 invariants preserved:
|
|
14
|
+
* * R224 tabular-nums fontVariantNumeric
|
|
15
|
+
* * R220 letter-spacing 0px → 0.4px on pin/hot
|
|
16
|
+
* * fontWeight=700 + fontFamily=monospace
|
|
17
|
+
* * textAnchor="middle"
|
|
18
|
+
* * data-edge-badge-text={link.key} surface attr
|
|
19
|
+
*
|
|
20
|
+
* Test mounts the dashboard with 1 message → 1 flow link → 1 badge.
|
|
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) => ({
|
|
38
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
|
|
43
|
+
});
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
46
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
47
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
48
|
+
] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForSelector('[data-edge-badge-text]', { timeout: 15000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const t = document.querySelector('[data-edge-badge-text]');
|
|
58
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
59
|
+
return {
|
|
60
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
61
|
+
fontSizeData: t?.getAttribute('data-edge-badge-text-font-size') ?? null,
|
|
62
|
+
fontWeight: t?.getAttribute('font-weight') ?? null,
|
|
63
|
+
fontFamily: t?.getAttribute('font-family') ?? null,
|
|
64
|
+
textAnchor: t?.getAttribute('text-anchor') ?? null,
|
|
65
|
+
linkKey: t?.getAttribute('data-edge-badge-text') ?? null,
|
|
66
|
+
pinAttr: t?.getAttribute('data-edge-badge-text-pin') ?? null,
|
|
67
|
+
fontVariant: cs?.fontVariantNumeric ?? null,
|
|
68
|
+
letterSpacing: cs?.letterSpacing ?? null,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const isRestLs = (v) => v === 'normal' || v === '0px' || v === '0' || v === null;
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
font_size_attr_11: probe.fontSizeAttr === '11',
|
|
78
|
+
data_font_size_11: probe.fontSizeData === '11',
|
|
79
|
+
font_weight_700: probe.fontWeight === '700',
|
|
80
|
+
font_family_mono: /monospace/i.test(probe.fontFamily || ''),
|
|
81
|
+
text_anchor_middle: probe.textAnchor === 'middle',
|
|
82
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
83
|
+
pin_attr_false: probe.pinAttr === 'false', // single-flow, not isHot
|
|
84
|
+
tabular_nums: /tabular-nums/.test(probe.fontVariant || ''), // R224
|
|
85
|
+
rest_letter_spacing: isRestLs(probe.letterSpacing), // R220 rest = 0
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} edge badge fontSize 10 → 11:`, JSON.stringify(results),
|
|
89
|
+
'\n probe:', probe);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* Round 371 verification: edge-badge cyber opacity 0.82 → 0.85.
|
|
2
|
+
* Sibling theme-consistency polish to R370 hub hover-ring (cyber
|
|
3
|
+
* 0.7 → 0.8). R251 designed the badge at 0.82 (cyber) / 0.95 (light)
|
|
4
|
+
* — a 13 % delta. R371 narrows to 10 %, bringing cyber closer to
|
|
5
|
+
* light theme's 0.95 floor. Light stays at 0.95.
|
|
6
|
+
*
|
|
7
|
+
* Contract (cyber):
|
|
8
|
+
* - Edge badge circle opacity attr === '0.85'.
|
|
9
|
+
* - data-edge-badge-opacity === '0.85'.
|
|
10
|
+
* - Pre-R371 invariants:
|
|
11
|
+
* * R164 r=9 (cold)
|
|
12
|
+
* * R367 strokeWidth=1.25 (cold rest)
|
|
13
|
+
* * R251 transition list (fill + opacity 200ms + r + stroke
|
|
14
|
+
* 300ms) intact
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const browser = await chromium.launch({ headless: true });
|
|
21
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
22
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
23
|
+
await ctx.addInitScript(() => {
|
|
24
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
25
|
+
});
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
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) => ({
|
|
32
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
|
|
37
|
+
});
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
40
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
41
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
42
|
+
] } }));
|
|
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-edge-badge-stroke-width-rest]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(300);
|
|
49
|
+
|
|
50
|
+
const probe = await page.evaluate(() => {
|
|
51
|
+
const circle = document.querySelector('[data-edge-badge-stroke-width-rest]');
|
|
52
|
+
const cs = circle ? getComputedStyle(circle) : null;
|
|
53
|
+
return {
|
|
54
|
+
opacityAttr: circle?.getAttribute('opacity') ?? null,
|
|
55
|
+
opacityData: circle?.getAttribute('data-edge-badge-opacity') ?? null,
|
|
56
|
+
rAttr: circle?.getAttribute('r') ?? null,
|
|
57
|
+
strokeWidthAttr: circle?.getAttribute('stroke-width') ?? null,
|
|
58
|
+
transition: cs?.transition ?? null,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await browser.close();
|
|
63
|
+
|
|
64
|
+
const hasTrans = (s, prop) =>
|
|
65
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
66
|
+
|
|
67
|
+
const results = {
|
|
68
|
+
opacity_attr_0_85: probe.opacityAttr === '0.85',
|
|
69
|
+
data_opacity_0_85: probe.opacityData === '0.85',
|
|
70
|
+
r_9_cold: probe.rAttr === '9', // R164 cold
|
|
71
|
+
stroke_width_1_25: probe.strokeWidthAttr === '1.25', // R367
|
|
72
|
+
trans_has_opacity: hasTrans(probe.transition, 'opacity'),
|
|
73
|
+
trans_has_fill: hasTrans(probe.transition, 'fill'),
|
|
74
|
+
trans_has_strokew: hasTrans(probe.transition, 'stroke-width'),
|
|
75
|
+
trans_has_r: hasTrans(probe.transition, 'r'),
|
|
76
|
+
};
|
|
77
|
+
const ok = Object.values(results).every(Boolean);
|
|
78
|
+
console.log(`${ok ? '✅' : '❌'} edge badge cyber opacity 0.82 → 0.85:`, JSON.stringify(results),
|
|
79
|
+
'\n probe:', probe);
|
|
80
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* Round 367 verification: edge midpoint badge rest stroke-width
|
|
2
|
+
* 1 → 1.25. Sibling visual-weight bump (7th canvas anchor in the
|
|
3
|
+
* R287/R295/R359/R360/R361/R365/R367 family).
|
|
4
|
+
*
|
|
5
|
+
* Cold edge badges gain ~25 % stroke presence. Stays clear of the
|
|
6
|
+
* R51 overlap-test sentinel values (1.5 / 3 reserved for node
|
|
7
|
+
* strokes — the test selector is gated to g[data-node] ancestors
|
|
8
|
+
* so this edge-internal circle is invisible to that probe anyway).
|
|
9
|
+
* R188 transition list 'stroke-width 300ms ease-out' still smoothes
|
|
10
|
+
* the hot/pin flip — now 1.25 → 2 instead of 1 → 2.
|
|
11
|
+
*
|
|
12
|
+
* Contract (cyber, fixture with cold flow):
|
|
13
|
+
* - Edge badge circle stroke-width attr === '1.25'.
|
|
14
|
+
* - data-edge-badge-stroke-width-rest === '1.25'.
|
|
15
|
+
* - data-edge-badge-lifted === 'false' (rest, not hovered/pinned).
|
|
16
|
+
* - data-edge-badge-text-pin === 'false' (cold flow, link.count<10).
|
|
17
|
+
* - Inline transition contains 'stroke-width' (R188 preserved).
|
|
18
|
+
* - Pre-R367 invariants: r=9 (cold, R164), fill=pal.legendBox.fill,
|
|
19
|
+
* stroke=pal.flowEdge (cold), opacity 0.82 (cyber).
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
|
|
42
|
+
});
|
|
43
|
+
// 1 message → 1 cold flow (count=1, well below R129 isHot=10).
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
46
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
47
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
48
|
+
] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForSelector('[data-edge-badge-stroke-width-rest]', { timeout: 15000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
// Probe the cold/rest edge badge circle (the parent of data-edge-badge-text
|
|
58
|
+
// is the <g> wrapping circle + text; we want the circle sibling).
|
|
59
|
+
const circle = document.querySelector('[data-edge-badge-stroke-width-rest]');
|
|
60
|
+
const cs = circle ? getComputedStyle(circle) : null;
|
|
61
|
+
// Find the text in the same group to check pin state.
|
|
62
|
+
const grp = circle?.parentElement;
|
|
63
|
+
const text = grp?.querySelector('[data-edge-badge-text]');
|
|
64
|
+
return {
|
|
65
|
+
strokeWidthAttr: circle?.getAttribute('stroke-width') ?? null,
|
|
66
|
+
strokeWidthData: circle?.getAttribute('data-edge-badge-stroke-width-rest') ?? null,
|
|
67
|
+
liftedAttr: circle?.getAttribute('data-edge-badge-lifted') ?? null,
|
|
68
|
+
rAttr: circle?.getAttribute('r') ?? null,
|
|
69
|
+
opacityAttr: circle?.getAttribute('opacity') ?? null,
|
|
70
|
+
transition: cs?.transition ?? null,
|
|
71
|
+
pinAttr: text?.getAttribute('data-edge-badge-text-pin') ?? null,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const hasTrans = (s, prop) =>
|
|
78
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
stroke_width_1_25: probe.strokeWidthAttr === '1.25',
|
|
82
|
+
data_stroke_1_25: probe.strokeWidthData === '1.25',
|
|
83
|
+
lifted_false_rest: probe.liftedAttr === 'false',
|
|
84
|
+
r_9_cold: probe.rAttr === '9', // R164 cold
|
|
85
|
+
opacity_0_82_cyber: probe.opacityAttr === '0.82', // R251 cyber theme
|
|
86
|
+
trans_has_strokew: hasTrans(probe.transition, 'stroke-width'), // R188
|
|
87
|
+
cold_flow_not_hot: probe.pinAttr === 'false', // R220 isHot/isPinned both false
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} edge badge stroke 1 → 1.25:`, JSON.stringify(results),
|
|
91
|
+
'\n probe:', probe);
|
|
92
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* Round 355 verification: filter pin pills (status / group / vendor)
|
|
2
|
+
* gain pure-CSS group-hover lift of their inner opacity-70 spans
|
|
3
|
+
* (prefix `filter:` + count `· N`) to 100 % on pill hover. Marks the
|
|
4
|
+
* whole pill as scan-it interactive without layout reflow (opacity
|
|
5
|
+
* is paint-only). The R335 prefix-opacity tier + R323 count-opacity
|
|
6
|
+
* tier are preserved at rest; both ease to 100 % on pill hover.
|
|
7
|
+
*
|
|
8
|
+
* Contract (cyber, status filter pinned to 'working'):
|
|
9
|
+
* - Rest: filter-prefix + filter-pill-count computed opacity 0.7.
|
|
10
|
+
* - Hover pill: both spans computed opacity 1.
|
|
11
|
+
* - Inline transition contains 'opacity'.
|
|
12
|
+
* - Pre-R355 invariants:
|
|
13
|
+
* * R335 prefix span keeps `hidden sm:inline opacity-70` baseline
|
|
14
|
+
* * R323 count span keeps `opacity-70 tabular-nums` baseline
|
|
15
|
+
* * Pill click-to-clear unchanged (still has onClick / cursor)
|
|
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 {
|
|
26
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
27
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
28
|
+
// Pre-pin status='working' so the filter pill mounts on first paint.
|
|
29
|
+
localStorage.setItem('anet-pinnedStatus', JSON.stringify('working'));
|
|
30
|
+
} 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) => ({
|
|
38
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
// If pre-pin via localStorage didn't take, click the working chip to pin.
|
|
50
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 15000 });
|
|
51
|
+
const pillVisible = await page.locator('[data-active-filter="status"]').count();
|
|
52
|
+
if (pillVisible === 0) {
|
|
53
|
+
await page.click('[data-working-chip]');
|
|
54
|
+
await page.waitForSelector('[data-active-filter="status"]', { timeout: 5000 });
|
|
55
|
+
}
|
|
56
|
+
await page.waitForTimeout(300);
|
|
57
|
+
|
|
58
|
+
const restProbe = await page.evaluate(() => {
|
|
59
|
+
const pill = document.querySelector('[data-active-filter="status"]');
|
|
60
|
+
const prefix = pill?.querySelector('[data-filter-prefix]');
|
|
61
|
+
const count = pill?.querySelector('[data-filter-pill-count]');
|
|
62
|
+
return {
|
|
63
|
+
prefixOpacity: prefix ? getComputedStyle(prefix).opacity : null,
|
|
64
|
+
countOpacity: count ? getComputedStyle(count).opacity : null,
|
|
65
|
+
prefixTrans: prefix ? getComputedStyle(prefix).transition : null,
|
|
66
|
+
countTrans: count ? getComputedStyle(count).transition : null,
|
|
67
|
+
prefixCls: prefix?.getAttribute('class') ?? null,
|
|
68
|
+
countCls: count?.getAttribute('class') ?? null,
|
|
69
|
+
pillCursor: pill ? getComputedStyle(pill).cursor : null,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Hover the pill.
|
|
74
|
+
await page.hover('[data-active-filter="status"]');
|
|
75
|
+
await page.waitForTimeout(300);
|
|
76
|
+
const hoverProbe = await page.evaluate(() => {
|
|
77
|
+
const pill = document.querySelector('[data-active-filter="status"]');
|
|
78
|
+
const prefix = pill?.querySelector('[data-filter-prefix]');
|
|
79
|
+
const count = pill?.querySelector('[data-filter-pill-count]');
|
|
80
|
+
return {
|
|
81
|
+
prefixOpacity: prefix ? getComputedStyle(prefix).opacity : null,
|
|
82
|
+
countOpacity: count ? getComputedStyle(count).opacity : null,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
|
|
88
|
+
const approxEq = (a, b, tol = 0.02) => {
|
|
89
|
+
const x = parseFloat(a); const y = parseFloat(b);
|
|
90
|
+
return Number.isFinite(x) && Number.isFinite(y) && Math.abs(x - y) <= tol;
|
|
91
|
+
};
|
|
92
|
+
const hasTrans = (s, prop) =>
|
|
93
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
rest_prefix_0_7: approxEq(restProbe.prefixOpacity, 0.7),
|
|
97
|
+
rest_count_0_7: approxEq(restProbe.countOpacity, 0.7),
|
|
98
|
+
prefix_trans_has_op: hasTrans(restProbe.prefixTrans, 'opacity'),
|
|
99
|
+
count_trans_has_op: hasTrans(restProbe.countTrans, 'opacity'),
|
|
100
|
+
prefix_cls_has_op_70: /opacity-70/.test(restProbe.prefixCls || ''), // R335
|
|
101
|
+
count_cls_has_tabular: /tabular-nums/.test(restProbe.countCls || ''), // R323
|
|
102
|
+
pill_cursor_pointer: restProbe.pillCursor === 'pointer',
|
|
103
|
+
hover_prefix_1: approxEq(hoverProbe.prefixOpacity, 1),
|
|
104
|
+
hover_count_1: approxEq(hoverProbe.countOpacity, 1),
|
|
105
|
+
};
|
|
106
|
+
const ok = Object.values(results).every(Boolean);
|
|
107
|
+
console.log(`${ok ? '✅' : '❌'} filter pill hover-opacity lift:`, JSON.stringify(results),
|
|
108
|
+
'\n rest: ', restProbe,
|
|
109
|
+
'\n hover:', hoverProbe);
|
|
110
|
+
process.exit(ok ? 0 : 1);
|