@sleep2agi/agent-network-dashboard 0.5.1-preview.4 → 0.5.1-preview.41
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/03s0-hiyr832o.js +4 -0
- package/.next/static/chunks/057c8hh9cri2y.js +1 -0
- package/.next/static/chunks/0hwb_h953qm4d.css +2 -0
- package/.next/static/chunks/0ulf.39xk5_1v.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +787 -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-box-stroke-test.mjs +105 -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 → 4Mzmc_vft-1RJtomdAHL-}/_buildManifest.js +0 -0
- /package/.next/static/{CkuUcpLXVJ-OsmjZqi3xX → 4Mzmc_vft-1RJtomdAHL-}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{CkuUcpLXVJ-OsmjZqi3xX → 4Mzmc_vft-1RJtomdAHL-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Round 364 verification: legend-row label fontWeight 400 → 500.
|
|
2
|
+
* Sibling typography lift to R363 recent-row text fw 400 → 500.
|
|
3
|
+
* Both surfaces render small monospace text against panel chrome
|
|
4
|
+
* at fontSize 9-11 where SVG-default fw 400 sits at the legibility
|
|
5
|
+
* floor.
|
|
6
|
+
*
|
|
7
|
+
* Family snapshot post-R364:
|
|
8
|
+
* legend label fw 500 (R364, this round)
|
|
9
|
+
* legend count fw 600 (R309)
|
|
10
|
+
* recent alias fw 500 (R363)
|
|
11
|
+
* recent count fw 600/700 (R320)
|
|
12
|
+
*
|
|
13
|
+
* Contract (cyber, fixture with active rows):
|
|
14
|
+
* - Each rendered [data-legend-row-label] has font-weight === '500'.
|
|
15
|
+
* - Each has data-legend-row-label-font-weight === '500'.
|
|
16
|
+
* - Pre-R364 invariants:
|
|
17
|
+
* * fontSize=11 + fontFamily=monospace
|
|
18
|
+
* * R219 letter-spacing 0 → 0.5px on pin (style.transition still
|
|
19
|
+
* lists letter-spacing)
|
|
20
|
+
* * R55 fill transition retained
|
|
21
|
+
* * data-legend-row-label key surfaces (working / idle / offline)
|
|
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-legend-row-label]', { timeout: 15000 });
|
|
51
|
+
await page.waitForTimeout(300);
|
|
52
|
+
|
|
53
|
+
const probe = await page.evaluate(() => {
|
|
54
|
+
const labels = Array.from(document.querySelectorAll('[data-legend-row-label]'));
|
|
55
|
+
return labels.map(el => {
|
|
56
|
+
const cs = getComputedStyle(el);
|
|
57
|
+
return {
|
|
58
|
+
key: el.getAttribute('data-legend-row-label'),
|
|
59
|
+
fontWeightAttr: el.getAttribute('font-weight'),
|
|
60
|
+
fontWeightData: el.getAttribute('data-legend-row-label-font-weight'),
|
|
61
|
+
fontSizeAttr: el.getAttribute('font-size'),
|
|
62
|
+
fontFamily: el.getAttribute('font-family'),
|
|
63
|
+
transition: cs.transition,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const hasTrans = (s, prop) =>
|
|
71
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
72
|
+
|
|
73
|
+
const allRowsOk = probe.length > 0 && probe.every(r =>
|
|
74
|
+
r.fontWeightAttr === '500'
|
|
75
|
+
&& r.fontWeightData === '500'
|
|
76
|
+
&& r.fontSizeAttr === '11'
|
|
77
|
+
&& /monospace/i.test(r.fontFamily || '')
|
|
78
|
+
&& hasTrans(r.transition, 'letter-spacing')
|
|
79
|
+
&& hasTrans(r.transition, 'fill')
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const results = {
|
|
83
|
+
rows_rendered: probe.length >= 1,
|
|
84
|
+
all_rows_fw_500: probe.every(r => r.fontWeightAttr === '500'),
|
|
85
|
+
all_rows_data_500: probe.every(r => r.fontWeightData === '500'),
|
|
86
|
+
all_rows_fontsize_11: probe.every(r => r.fontSizeAttr === '11'),
|
|
87
|
+
all_rows_monospace: probe.every(r => /monospace/i.test(r.fontFamily || '')),
|
|
88
|
+
all_rows_have_ls_trans: probe.every(r => hasTrans(r.transition, 'letter-spacing')),
|
|
89
|
+
all_rows_have_fill_trans: probe.every(r => hasTrans(r.transition, 'fill')),
|
|
90
|
+
};
|
|
91
|
+
const ok = allRowsOk && Object.values(results).every(Boolean);
|
|
92
|
+
console.log(`${ok ? '✅' : '❌'} legend-row label fw 400 → 500:`, JSON.stringify(results),
|
|
93
|
+
'\n rows: ', probe.map(r => ({ key: r.key, fw: r.fontWeightAttr })));
|
|
94
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 372 verification: minimap offline-dot opacity 0.5 → 0.6.
|
|
2
|
+
* Sibling stale-state legibility lift to R358 (freshness ramp floor
|
|
3
|
+
* 0.25 → 0.30). Pre-R372 R198 drew offline dots at α=0.5; R372 lifts
|
|
4
|
+
* to 0.6 (+20 % relative) for better readability while keeping a
|
|
5
|
+
* clear two-tier distinction vs online α=0.9.
|
|
6
|
+
*
|
|
7
|
+
* The minimap only renders when view is non-default (zoom !== 1 ||
|
|
8
|
+
* pan). Test triggers zoom-in to mount it, then probes the offline
|
|
9
|
+
* dot's opacity attr.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - data-topo-minimap-dot-online="false" element opacity === '0.6'.
|
|
13
|
+
* - data-topo-minimap-dot-opacity === '0.6' for offline dot.
|
|
14
|
+
* - Online dot still at opacity '0.9' (R198 invariant).
|
|
15
|
+
* - Pre-R372 invariants:
|
|
16
|
+
* * R198 offline r=1.2, online r=1.7 preserved
|
|
17
|
+
* * R198 transition list (opacity + fill + r) preserved
|
|
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
|
+
const stale = new Date(Date.now() - 600 * 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
|
+
// Mix of online (working) + offline sessions.
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
37
|
+
{ alias: 'on-a', status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
39
|
+
{ alias: 'on-b', status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
40
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
41
|
+
{ alias: 'off-c', status: 'offline', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
43
|
+
] } });
|
|
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
|
+
// Minimap only mounts when view is non-default — zoom in twice.
|
|
51
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
52
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
53
|
+
await page.waitForTimeout(200);
|
|
54
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
55
|
+
await page.waitForSelector('[data-topo-minimap-dot]', { timeout: 5000 });
|
|
56
|
+
await page.waitForTimeout(400);
|
|
57
|
+
|
|
58
|
+
const probe = await page.evaluate(() => {
|
|
59
|
+
const offline = document.querySelector('[data-topo-minimap-dot-online="false"]');
|
|
60
|
+
const online = document.querySelector('[data-topo-minimap-dot-online="true"]');
|
|
61
|
+
const cs = offline ? getComputedStyle(offline) : null;
|
|
62
|
+
return {
|
|
63
|
+
offlineOpacity: offline?.getAttribute('opacity') ?? null,
|
|
64
|
+
offlineData: offline?.getAttribute('data-topo-minimap-dot-opacity') ?? null,
|
|
65
|
+
offlineR: offline?.getAttribute('r') ?? null,
|
|
66
|
+
offlineTrans: cs?.transition ?? null,
|
|
67
|
+
onlineOpacity: online?.getAttribute('opacity') ?? null,
|
|
68
|
+
onlineR: online?.getAttribute('r') ?? null,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const hasTrans = (s, prop) =>
|
|
75
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
76
|
+
|
|
77
|
+
const results = {
|
|
78
|
+
offline_opacity_0_6: probe.offlineOpacity === '0.6',
|
|
79
|
+
offline_data_0_6: probe.offlineData === '0.6',
|
|
80
|
+
offline_r_1_2: probe.offlineR === '1.2', // R198 invariant
|
|
81
|
+
online_opacity_0_9: probe.onlineOpacity === '0.9', // R198 invariant
|
|
82
|
+
online_r_1_7: probe.onlineR === '1.7', // R198 invariant
|
|
83
|
+
trans_has_opacity: hasTrans(probe.offlineTrans, 'opacity'), // R198
|
|
84
|
+
trans_has_fill: hasTrans(probe.offlineTrans, 'fill'), // R198
|
|
85
|
+
trans_has_r: hasTrans(probe.offlineTrans, 'r'), // R198
|
|
86
|
+
};
|
|
87
|
+
const ok = Object.values(results).every(Boolean);
|
|
88
|
+
console.log(`${ok ? '✅' : '❌'} minimap offline opacity 0.5 → 0.6:`, JSON.stringify(results),
|
|
89
|
+
'\n probe:', probe);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* Round 346 verification: minimap viewport rect gains a hover-state
|
|
2
|
+
* affordance — strokeWidth 1.5 → 1.75 + opacity 0.9 → 1.0 when the
|
|
3
|
+
* user enters the minimap container. The minimap is a recenter click-
|
|
4
|
+
* target (role=button at line ~7810), but pre-R346 the only hover hint
|
|
5
|
+
* was `cursor: crosshair`. R346 lifts the viewport rect to mark "this
|
|
6
|
+
* is the recenter target and it's alive". Sibling polish to R332
|
|
7
|
+
* minimap rounded-md → rounded-lg corner family — that round refined
|
|
8
|
+
* geometry, this one gives the viewport indicator inside the geometry
|
|
9
|
+
* a hover state.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Rest: viewport rect stroke-width=1.5, opacity=0.9.
|
|
13
|
+
* - Hover container: stroke-width=1.75, opacity=1.
|
|
14
|
+
* - Inline transition list contains 'stroke-width' AND 'opacity'.
|
|
15
|
+
* - data-topo-minimap-hovered toggles 'false' → 'true' on enter.
|
|
16
|
+
* - data-topo-minimap-viewport-hover mirrors hover state on the rect.
|
|
17
|
+
* - Pre-R346 invariants intact: rect still data-topo-minimap-viewport,
|
|
18
|
+
* R287 stroke=1.5 rest value, R199 smoothView transition list
|
|
19
|
+
* extended (not replaced).
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('alpha-1'), mk('alpha-2'), mk('alpha-3'),
|
|
43
|
+
mk('beta-1'), mk('beta-2'), mk('beta-3'),
|
|
44
|
+
mk('gamma-1'), mk('gamma-2'), mk('gamma-3'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
// Minimap only mounts when view is non-default (zoom !== 1 or pan).
|
|
53
|
+
// Click zoom-in to trigger mount.
|
|
54
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
55
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
56
|
+
await page.waitForTimeout(200);
|
|
57
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
58
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
// Rest probe.
|
|
62
|
+
const restProbe = await page.evaluate(() => {
|
|
63
|
+
const rect = document.querySelector('[data-topo-minimap-viewport]');
|
|
64
|
+
const container = document.querySelector('[data-topo-minimap]');
|
|
65
|
+
return {
|
|
66
|
+
strokeWidth: rect?.getAttribute('stroke-width') ?? null,
|
|
67
|
+
opacity: rect?.getAttribute('opacity') ?? null,
|
|
68
|
+
transition: rect ? getComputedStyle(rect).transition : null,
|
|
69
|
+
rectHoverAttr: rect?.getAttribute('data-topo-minimap-viewport-hover') ?? null,
|
|
70
|
+
contHoverAttr: container?.getAttribute('data-topo-minimap-hovered') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Hover the minimap container.
|
|
75
|
+
await page.hover('[data-topo-minimap]');
|
|
76
|
+
await page.waitForTimeout(250);
|
|
77
|
+
const hoverProbe = await page.evaluate(() => {
|
|
78
|
+
const rect = document.querySelector('[data-topo-minimap-viewport]');
|
|
79
|
+
const container = document.querySelector('[data-topo-minimap]');
|
|
80
|
+
return {
|
|
81
|
+
strokeWidth: rect?.getAttribute('stroke-width') ?? null,
|
|
82
|
+
opacity: rect?.getAttribute('opacity') ?? null,
|
|
83
|
+
rectHoverAttr: rect?.getAttribute('data-topo-minimap-viewport-hover') ?? null,
|
|
84
|
+
contHoverAttr: container?.getAttribute('data-topo-minimap-hovered') ?? null,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const hasTrans = (s, prop) =>
|
|
91
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
rest_sw_1_5: restProbe.strokeWidth === '1.5',
|
|
95
|
+
rest_op_0_9: restProbe.opacity === '0.9',
|
|
96
|
+
rest_cont_false: restProbe.contHoverAttr === 'false',
|
|
97
|
+
rest_rect_false: restProbe.rectHoverAttr === 'false',
|
|
98
|
+
trans_has_strokew: hasTrans(restProbe.transition, 'stroke-width'),
|
|
99
|
+
trans_has_opacity: hasTrans(restProbe.transition, 'opacity'),
|
|
100
|
+
hover_sw_1_75: hoverProbe.strokeWidth === '1.75',
|
|
101
|
+
hover_op_1: hoverProbe.opacity === '1',
|
|
102
|
+
hover_cont_true: hoverProbe.contHoverAttr === 'true',
|
|
103
|
+
hover_rect_true: hoverProbe.rectHoverAttr === 'true',
|
|
104
|
+
};
|
|
105
|
+
const ok = Object.values(results).every(Boolean);
|
|
106
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport hover affordance:`, JSON.stringify(results),
|
|
107
|
+
'\n rest: ', restProbe,
|
|
108
|
+
'\n hover:', hoverProbe);
|
|
109
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* Round 379 verification: minimap viewport rect picks up
|
|
2
|
+
* strokeLinejoin='round'. Sibling SVG stroke-softening polish to
|
|
3
|
+
* R378 flow-rail linecap + R288 chrome icon linecap family.
|
|
4
|
+
*
|
|
5
|
+
* The minimap only mounts when view is non-default (zoom !== 1 ||
|
|
6
|
+
* pan), so test triggers zoom-in to mount it.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - [data-topo-minimap-viewport] element stroke-linejoin attr === 'round'.
|
|
10
|
+
* - data-topo-minimap-viewport-linejoin === 'round'.
|
|
11
|
+
* - Pre-R379 invariants:
|
|
12
|
+
* * R287 strokeWidth=1.5 (cold rest)
|
|
13
|
+
* * R287 stroke=pal.legendAccent
|
|
14
|
+
* * R346 opacity=0.9 (cold rest)
|
|
15
|
+
* * R199 smoothView + R346 transition list still tween correctly
|
|
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
|
+
// Trigger zoom-in to mount minimap (R30 gate).
|
|
45
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
|
|
46
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
47
|
+
await page.waitForTimeout(200);
|
|
48
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
49
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
50
|
+
await page.waitForTimeout(400);
|
|
51
|
+
|
|
52
|
+
const probe = await page.evaluate(() => {
|
|
53
|
+
const r = document.querySelector('[data-topo-minimap-viewport]');
|
|
54
|
+
return {
|
|
55
|
+
linejoinAttr: r?.getAttribute('stroke-linejoin') ?? null,
|
|
56
|
+
linejoinData: r?.getAttribute('data-topo-minimap-viewport-linejoin') ?? null,
|
|
57
|
+
strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
|
|
58
|
+
opacityAttr: r?.getAttribute('opacity') ?? null,
|
|
59
|
+
fillAttr: r?.getAttribute('fill') ?? null,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await browser.close();
|
|
64
|
+
|
|
65
|
+
const results = {
|
|
66
|
+
linejoin_round: probe.linejoinAttr === 'round',
|
|
67
|
+
data_linejoin_round: probe.linejoinData === 'round',
|
|
68
|
+
stroke_width_1_5_rest: probe.strokeWidthAttr === '1.5', // R287 cold rest
|
|
69
|
+
opacity_0_9_rest: probe.opacityAttr === '0.9', // R346 cold rest
|
|
70
|
+
fill_none: probe.fillAttr === 'none', // viewport rect is stroke-only
|
|
71
|
+
};
|
|
72
|
+
const ok = Object.values(results).every(Boolean);
|
|
73
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport linejoin='round':`, JSON.stringify(results),
|
|
74
|
+
'\n probe:', probe);
|
|
75
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 368 verification: recent-panel '+N more flows' footer text
|
|
2
|
+
* fontWeight 400 → 500. Sibling small-text fw lift family with R363
|
|
3
|
+
* recent-row alias + R364 legend-row label + R366 group-label count
|
|
4
|
+
* — all four lifts thicken small-fontSize text (9-11 px) against
|
|
5
|
+
* panel chrome from SVG-default 400 to the font-medium tier (500).
|
|
6
|
+
*
|
|
7
|
+
* The footer only renders when flowLinks.length > 3 (the panel shows
|
|
8
|
+
* the 3 most recent + a footer link to /messages for the overflow).
|
|
9
|
+
*
|
|
10
|
+
* Contract (cyber, 5+ flow links so the footer mounts):
|
|
11
|
+
* - data-recent-panel-more text element font-weight === '500'.
|
|
12
|
+
* - data-recent-panel-more-font-weight === '500'.
|
|
13
|
+
* - Pre-R368 invariants on the same element:
|
|
14
|
+
* * fontSize='9' + fontFamily='monospace' + fontStyle='italic'
|
|
15
|
+
* * letter-spacing='0.2' (R325 rest baseline)
|
|
16
|
+
* * opacity='0.55' (rest, R325 era)
|
|
17
|
+
* * inline transition lists opacity + letter-spacing + fill
|
|
18
|
+
* * data-recent-panel-more attr surfaces the count
|
|
19
|
+
* * data-recent-panel-more-unit tspan with opacity 0.7 (R340)
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('a'), mk('b'), mk('c'), mk('d'), mk('e'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
// 5 distinct flow links → panel shows 3 + footer "+ 2 more flows".
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const pairs = [['a','b'], ['c','d'], ['a','c'], ['b','d'], ['e','a']];
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages:
|
|
49
|
+
pairs.map(([from, to], idx) => ({
|
|
50
|
+
id: `m${idx}`, from_alias: from, to_alias: to, content: `ping ${idx}`,
|
|
51
|
+
network_id: 'default',
|
|
52
|
+
created_at: new Date(now - (5000 + idx * 1000)).toISOString(),
|
|
53
|
+
}))
|
|
54
|
+
} }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
56
|
+
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const t = document.querySelector('[data-recent-panel-more]');
|
|
64
|
+
const unit = document.querySelector('[data-recent-panel-more-unit]');
|
|
65
|
+
const cs = t ? getComputedStyle(t) : null;
|
|
66
|
+
return {
|
|
67
|
+
fontWeightAttr: t?.getAttribute('font-weight') ?? null,
|
|
68
|
+
fontWeightData: t?.getAttribute('data-recent-panel-more-font-weight') ?? null,
|
|
69
|
+
fontSizeAttr: t?.getAttribute('font-size') ?? null,
|
|
70
|
+
fontFamily: t?.getAttribute('font-family') ?? null,
|
|
71
|
+
fontStyle: t?.getAttribute('font-style') ?? null,
|
|
72
|
+
letterSpacing: t?.getAttribute('letter-spacing') ?? null,
|
|
73
|
+
opacityAttr: t?.getAttribute('opacity') ?? null,
|
|
74
|
+
moreCount: t?.getAttribute('data-recent-panel-more') ?? null,
|
|
75
|
+
hoveredAttr: t?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
76
|
+
transition: cs?.transition ?? null,
|
|
77
|
+
unitOpacity: unit?.getAttribute('opacity') ?? null,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await browser.close();
|
|
82
|
+
|
|
83
|
+
const hasTrans = (s, prop) =>
|
|
84
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
85
|
+
|
|
86
|
+
const results = {
|
|
87
|
+
font_weight_500: probe.fontWeightAttr === '500',
|
|
88
|
+
data_fw_500: probe.fontWeightData === '500',
|
|
89
|
+
font_size_9: probe.fontSizeAttr === '9',
|
|
90
|
+
font_family_mono: /monospace/i.test(probe.fontFamily || ''),
|
|
91
|
+
font_style_italic: probe.fontStyle === 'italic', // pre-R368
|
|
92
|
+
letter_spacing_0_2: probe.letterSpacing === '0.2', // R325 rest
|
|
93
|
+
opacity_0_55_rest: probe.opacityAttr === '0.55', // R325 era rest
|
|
94
|
+
more_count_2: probe.moreCount === '2', // 5 links - 3 shown
|
|
95
|
+
hovered_false_rest: probe.hoveredAttr === 'false',
|
|
96
|
+
trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'),
|
|
97
|
+
trans_has_fill: hasTrans(probe.transition, 'fill'),
|
|
98
|
+
unit_opacity_0_7: probe.unitOpacity === '0.7', // R340 unit split
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} more-flows footer fw 400 → 500:`, JSON.stringify(results),
|
|
102
|
+
'\n probe:', probe);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* Round 344 verification: +N more flows footer hover state adds
|
|
2
|
+
* letter-spacing tween 0.2 → 0.3. Layers on top of R195 cyan fill +
|
|
3
|
+
* R325 baseline letter-spacing 0.2 + R133 underline + R325 opacity
|
|
4
|
+
* 0.55 → 0.85 hover. Sibling to R218/R219/R220 pin-signature letter-
|
|
5
|
+
* spacing family applied to a hover-only surface.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Rest state: [data-recent-panel-more] letter-spacing="0.2".
|
|
9
|
+
* - Hover state: letter-spacing="0.3" (after mouseover).
|
|
10
|
+
* - Inline style.transition contains 'letter-spacing'.
|
|
11
|
+
* - R325 + R259 regressions intact.
|
|
12
|
+
* - R317/R318 chrome regression.
|
|
13
|
+
*/
|
|
14
|
+
import { chromium } from 'playwright';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
18
|
+
const browser = await chromium.launch({ headless: true });
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias) => ({
|
|
30
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
35
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'), mk('epsilon'),
|
|
36
|
+
] } });
|
|
37
|
+
});
|
|
38
|
+
// 5 flow pairs → footer mounts (need >3 flowLinks).
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const mkMsg = (idx, from_alias, to_alias) => ({
|
|
41
|
+
id: `${from_alias}-${to_alias}-${idx}`,
|
|
42
|
+
from_alias, to_alias, content: `m${idx}`,
|
|
43
|
+
network_id: 'default',
|
|
44
|
+
created_at: new Date(now - (5 + idx) * 1000).toISOString(),
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
47
|
+
mkMsg(1, 'alpha', 'beta'),
|
|
48
|
+
mkMsg(2, 'beta', 'gamma'),
|
|
49
|
+
mkMsg(3, 'gamma', 'delta'),
|
|
50
|
+
mkMsg(4, 'delta', 'epsilon'),
|
|
51
|
+
mkMsg(5, 'epsilon', 'alpha'),
|
|
52
|
+
] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
// Rest-state probe.
|
|
61
|
+
const restProbe = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-recent-panel-more]');
|
|
63
|
+
return {
|
|
64
|
+
letterSpacing: el?.getAttribute('letter-spacing') ?? null,
|
|
65
|
+
transition: el ? getComputedStyle(el).transition : null,
|
|
66
|
+
hovered: el?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Hover the parent <g> wrapper — the onMouseEnter is on the link group.
|
|
71
|
+
await page.hover('[data-recent-panel-more-nav]');
|
|
72
|
+
await page.waitForTimeout(300);
|
|
73
|
+
|
|
74
|
+
const hoverProbe = await page.evaluate(() => {
|
|
75
|
+
const el = document.querySelector('[data-recent-panel-more]');
|
|
76
|
+
return {
|
|
77
|
+
letterSpacing: el?.getAttribute('letter-spacing') ?? null,
|
|
78
|
+
hovered: el?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const hasLetterSpacingTrans = (s) =>
|
|
85
|
+
/letter-spacing\s+\d*\.?\d*s|letter-spacing\s+\d+ms/i.test(s || '');
|
|
86
|
+
|
|
87
|
+
const results = {
|
|
88
|
+
rest_letter_spacing_0_2: restProbe.letterSpacing === '0.2',
|
|
89
|
+
rest_not_hovered: restProbe.hovered === 'false',
|
|
90
|
+
hover_letter_spacing_0_3: hoverProbe.letterSpacing === '0.3',
|
|
91
|
+
hover_state_true: hoverProbe.hovered === 'true',
|
|
92
|
+
transition_has_letter_sp: hasLetterSpacingTrans(restProbe.transition),
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} more-flows hover letter-spacing:`, JSON.stringify(results),
|
|
96
|
+
'\n rest:', restProbe,
|
|
97
|
+
'\n hover:', hoverProbe);
|
|
98
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 349 verification: panel header counts pick up editorial
|
|
2
|
+
* letter-spacing 0.2, one tier below the R301 panel title 0.3.
|
|
3
|
+
* Closes the panel-pair editorial symmetry — both top-corner panels
|
|
4
|
+
* now have a 2-step header hierarchy:
|
|
5
|
+
* title fontSize=12 fontWeight=700 letterSpacing=0.3 (R301)
|
|
6
|
+
* count fontSize=10 fontWeight=600 letterSpacing=0.2 (R349, this round)
|
|
7
|
+
* Joins the R285 / R289 / R301 / R302 / R304 / R325 editorial-
|
|
8
|
+
* letterspacing tier at the panel-summary scope. The R162 freshness
|
|
9
|
+
* fill, R225 tabular-nums, R311 fontWeight, R336 unit-tspan opacity-0.7
|
|
10
|
+
* split all preserved (SVG inheritance propagates the tier to descendant
|
|
11
|
+
* tspans).
|
|
12
|
+
*
|
|
13
|
+
* Contract:
|
|
14
|
+
* - Recent-signal panel header count <text> letter-spacing="0.2".
|
|
15
|
+
* - Legend panel header count <text> letter-spacing="0.2".
|
|
16
|
+
* - data-{recent,legend}-panel-count-letter-spacing="0.2" attrs present.
|
|
17
|
+
* - Pre-R349 invariants: R311 fontWeight=600, R225 tabular-nums,
|
|
18
|
+
* R336 unit-tspan opacity=0.7 all preserved.
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b'), mk('c') ] } });
|
|
41
|
+
});
|
|
42
|
+
// One message → recent panel mounts.
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
45
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
46
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
47
|
+
] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForSelector('[data-recent-panel-count]', { timeout: 15000 });
|
|
53
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 5000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const rTspan = document.querySelector('[data-recent-panel-count]');
|
|
58
|
+
const rText = rTspan?.closest('text');
|
|
59
|
+
const rUnit = document.querySelector('[data-recent-panel-count-unit]');
|
|
60
|
+
const lText = document.querySelector('[data-legend-panel-count]');
|
|
61
|
+
const lUnit = document.querySelector('[data-legend-panel-count-unit]');
|
|
62
|
+
return {
|
|
63
|
+
recentTextLs: rText?.getAttribute('letter-spacing') ?? null,
|
|
64
|
+
recentLsAttr: rText?.getAttribute('data-recent-panel-count-letter-spacing') ?? null,
|
|
65
|
+
recentFw: rTspan?.getAttribute('font-weight') ?? null,
|
|
66
|
+
recentUnitOp: rUnit?.getAttribute('opacity') ?? null,
|
|
67
|
+
legendTextLs: lText?.getAttribute('letter-spacing') ?? null,
|
|
68
|
+
legendLsAttr: lText?.getAttribute('data-legend-panel-count-letter-spacing') ?? null,
|
|
69
|
+
legendFw: lText?.getAttribute('font-weight') ?? null,
|
|
70
|
+
legendUnitOp: lUnit?.getAttribute('opacity') ?? null,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
recent_text_ls_0_2: probe.recentTextLs === '0.2',
|
|
78
|
+
recent_attr_0_2: probe.recentLsAttr === '0.2',
|
|
79
|
+
recent_fw_600: probe.recentFw === '600', // R311
|
|
80
|
+
recent_unit_op_0_7: probe.recentUnitOp === '0.7', // R336
|
|
81
|
+
legend_text_ls_0_2: probe.legendTextLs === '0.2',
|
|
82
|
+
legend_attr_0_2: probe.legendLsAttr === '0.2',
|
|
83
|
+
legend_fw_600: probe.legendFw === '600', // R310
|
|
84
|
+
legend_unit_op_0_7: probe.legendUnitOp === '0.7', // R336
|
|
85
|
+
};
|
|
86
|
+
const ok = Object.values(results).every(Boolean);
|
|
87
|
+
console.log(`${ok ? '✅' : '❌'} panel count letter-spacing 0.2:`, JSON.stringify(results),
|
|
88
|
+
'\n probe:', probe);
|
|
89
|
+
process.exit(ok ? 0 : 1);
|