@sleep2agi/agent-network-dashboard 0.4.6-preview.74 → 0.4.6-preview.76

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.
Files changed (158) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +31 -31
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +11 -11
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +6 -6
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +13 -13
  25. package/.next/server/app/admin.segments/_full.segment.rsc +13 -13
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +6 -6
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +13 -13
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +13 -13
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +6 -6
  37. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +13 -13
  41. package/.next/server/app/login.segments/_full.segment.rsc +13 -13
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +6 -6
  44. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +2 -2
  49. package/.next/server/app/logs.rsc +13 -13
  50. package/.next/server/app/logs.segments/_full.segment.rsc +13 -13
  51. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/logs.segments/_index.segment.rsc +6 -6
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +2 -2
  58. package/.next/server/app/messages.rsc +13 -13
  59. package/.next/server/app/messages.segments/_full.segment.rsc +13 -13
  60. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/messages.segments/_index.segment.rsc +6 -6
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +2 -2
  67. package/.next/server/app/node.rsc +13 -13
  68. package/.next/server/app/node.segments/_full.segment.rsc +13 -13
  69. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/node.segments/_index.segment.rsc +6 -6
  71. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +2 -2
  76. package/.next/server/app/nodes.rsc +13 -13
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +13 -13
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +6 -6
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  83. package/.next/server/app/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs.html +2 -2
  86. package/.next/server/app/server-logs.rsc +13 -13
  87. package/.next/server/app/server-logs.segments/_full.segment.rsc +13 -13
  88. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  89. package/.next/server/app/server-logs.segments/_index.segment.rsc +6 -6
  90. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  92. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  93. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/settings/networks.html +2 -2
  95. package/.next/server/app/settings/networks.rsc +13 -13
  96. package/.next/server/app/settings/networks.segments/_full.segment.rsc +13 -13
  97. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/settings/networks.segments/_index.segment.rsc +6 -6
  99. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  100. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  103. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  105. package/.next/server/app/settings/tokens.html +2 -2
  106. package/.next/server/app/settings/tokens.rsc +13 -13
  107. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +13 -13
  108. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  109. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +6 -6
  110. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  111. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  112. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  113. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  114. package/.next/server/app/settings.html +2 -2
  115. package/.next/server/app/settings.rsc +13 -13
  116. package/.next/server/app/settings.segments/_full.segment.rsc +13 -13
  117. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings.segments/_index.segment.rsc +6 -6
  119. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  120. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  122. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/tasks.html +2 -2
  125. package/.next/server/app/tasks.rsc +13 -13
  126. package/.next/server/app/tasks.segments/_full.segment.rsc +13 -13
  127. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  128. package/.next/server/app/tasks.segments/_index.segment.rsc +6 -6
  129. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  130. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  131. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  132. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +1 -1
  133. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  134. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  135. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  136. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  140. package/.next/server/middleware-build-manifest.js +3 -3
  141. package/.next/server/pages/404.html +2 -2
  142. package/.next/server/pages/500.html +1 -1
  143. package/.next/server/server-reference-manifest.js +1 -1
  144. package/.next/server/server-reference-manifest.json +1 -1
  145. package/.next/static/chunks/{0__-l50jc2og..js → 06s.1x-exvzuc.js} +1 -1
  146. package/.next/static/chunks/{0lk_jpbgmnf0d.js → 0fkgp5kpvst~1.js} +2 -2
  147. package/.next/static/chunks/{0ecpazqrnb.0k.js → 15uu1jm-rad24.js} +1 -1
  148. package/.next/static/chunks/{0-rg_sn64~6ct.js → 17~3-izo0llut.js} +1 -1
  149. package/.next/trace +2 -2
  150. package/.next/trace-build +1 -1
  151. package/app/components/CommandPalette.tsx +33 -0
  152. package/app/components/TopoGraph.tsx +41 -4
  153. package/package.json +1 -1
  154. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  155. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  156. /package/.next/static/{Xd4MkQH8ezFJVT3r4zsAA → as6EDAHmUnO6SrdXrgkfz}/_buildManifest.js +0 -0
  157. /package/.next/static/{Xd4MkQH8ezFJVT3r4zsAA → as6EDAHmUnO6SrdXrgkfz}/_clientMiddlewareManifest.js +0 -0
  158. /package/.next/static/{Xd4MkQH8ezFJVT3r4zsAA → as6EDAHmUnO6SrdXrgkfz}/_ssgManifest.js +0 -0
@@ -0,0 +1,123 @@
1
+ /* Round 69 verification: Cmd+K palette can pin topology filters from
2
+ * anywhere. The palette dispatches a CustomEvent('anet:topo-pin') that
3
+ * TopoGraph listens for and translates into pinnedStatus / pinnedGroup
4
+ * state. sessionStorage is written in lockstep so a reload preserves it
5
+ * (R66 carries it through).
6
+ *
7
+ * - Dispatch {kind:'status', value:'working'} → pill appears + idle/
8
+ * offline nodes dim. sessionStorage holds 'working'.
9
+ * - Dispatch {kind:'clear'} → pill disappears + opacities restore.
10
+ * sessionStorage entries cleared.
11
+ * - Dispatch {kind:'group', value:'alpha'} → group pill + opacities
12
+ * composed; storage holds 'alpha'.
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
+
20
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(() => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', 'grid');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ sessionStorage.removeItem('anet-topo-pinned-status');
28
+ sessionStorage.removeItem('anet-topo-pinned-group');
29
+ } catch {}
30
+ });
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 sessions = [
38
+ { alias: 'alpha1', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ { alias: 'alpha2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
40
+ { alias: 'alpha3', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
41
+ { alias: 'beta', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
42
+ ];
43
+ await route.fulfill({ response: r, json: { ...b, sessions } });
44
+ });
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
46
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
50
+ await page.waitForTimeout(500);
51
+
52
+ const snapshot = () => page.evaluate(() => {
53
+ const pills = [...document.querySelectorAll('[data-active-filter]')].map(el => ({
54
+ kind: el.getAttribute('data-active-filter'),
55
+ text: (el.innerText || el.textContent || '').trim(),
56
+ }));
57
+ const opacities = {};
58
+ for (const a of ['alpha1', 'alpha2', 'beta']) {
59
+ const g = document.querySelector(`g[data-node="${a}"]`);
60
+ opacities[a] = g ? +(g.style.opacity || '1') : null;
61
+ }
62
+ return {
63
+ pills,
64
+ opacities,
65
+ storageStatus: sessionStorage.getItem('anet-topo-pinned-status'),
66
+ storageGroup: sessionStorage.getItem('anet-topo-pinned-group'),
67
+ };
68
+ });
69
+
70
+ const before = await snapshot();
71
+
72
+ // Dispatch palette pin: status=working
73
+ await page.evaluate(() => {
74
+ // The Cmd+K commands write to storage AND dispatch the event in
75
+ // lockstep. We mirror both here so we exercise the same code path
76
+ // a palette `perform()` would take.
77
+ sessionStorage.setItem('anet-topo-pinned-status', 'working');
78
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'status', value: 'working' } }));
79
+ });
80
+ await page.waitForTimeout(250);
81
+ const afterStatus = await snapshot();
82
+
83
+ // Dispatch palette pin: group=alpha (additionally)
84
+ await page.evaluate(() => {
85
+ sessionStorage.setItem('anet-topo-pinned-group', 'alpha');
86
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: 'alpha' } }));
87
+ });
88
+ await page.waitForTimeout(250);
89
+ const afterGroup = await snapshot();
90
+
91
+ // Dispatch palette clear
92
+ await page.evaluate(() => {
93
+ sessionStorage.removeItem('anet-topo-pinned-status');
94
+ sessionStorage.removeItem('anet-topo-pinned-group');
95
+ window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear' } }));
96
+ });
97
+ await page.waitForTimeout(250);
98
+ const afterClear = await snapshot();
99
+
100
+ await browser.close();
101
+
102
+ const bright = (v) => v != null && v >= 0.55;
103
+ const dim = (v) => v != null && v < 0.4;
104
+ const results = {
105
+ before_noPills: before.pills.length === 0,
106
+ before_baselineBright: bright(before.opacities.alpha1) && bright(before.opacities.alpha2),
107
+ status_pillRendered: afterStatus.pills.some(p => p.kind === 'status' && /working/.test(p.text)),
108
+ status_dimsIdle: dim(afterStatus.opacities.alpha2) && dim(afterStatus.opacities.beta),
109
+ status_keepsWorking: bright(afterStatus.opacities.alpha1),
110
+ status_storageWritten: afterStatus.storageStatus === 'working',
111
+ groupAdded_bothPills: afterGroup.pills.length === 2,
112
+ groupAdded_storageBoth: afterGroup.storageStatus === 'working' && afterGroup.storageGroup === 'alpha',
113
+ clear_noPills: afterClear.pills.length === 0,
114
+ clear_restoredOpacities: bright(afterClear.opacities.alpha1) && bright(afterClear.opacities.alpha2) && bright(afterClear.opacities.beta),
115
+ clear_storageCleared: afterClear.storageStatus === null && afterClear.storageGroup === null,
116
+ };
117
+ const ok = Object.values(results).every(Boolean);
118
+ console.log(`${ok ? '✅' : '❌'} cmdk-pin:`, JSON.stringify(results),
119
+ `\n before=`, before,
120
+ `\n afterStatus=`, afterStatus,
121
+ `\n afterGroup=`, afterGroup,
122
+ `\n afterClear=`, afterClear);
123
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,95 @@
1
+ /* Round 68 verification: group-box rect has a 3-tier visual ladder:
2
+ * idle → strokeWidth 1.5, dashed, ringStroke colour
3
+ * hovered → strokeWidth 2, solid, legendAccent colour
4
+ * pinned → strokeWidth 3, solid, legendAccent colour (stronger)
5
+ *
6
+ * Plus `data-group-box-pinned` reflects the pinned state so external
7
+ * tooling can spot a pinned box without inspecting computed CSS.
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const browser = await chromium.launch({ headless: true });
14
+
15
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try {
19
+ localStorage.setItem('anet-theme', 'cyber');
20
+ localStorage.setItem('anet-topo-layout', 'grid');
21
+ sessionStorage.setItem('anet_v3_auth', '1');
22
+ } catch {}
23
+ });
24
+
25
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const sessions = [
31
+ { alias: 'alpha1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
32
+ { alias: 'alpha2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
33
+ { alias: 'alpha3', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
34
+ { alias: 'beta1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
35
+ { alias: 'beta2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
36
+ ];
37
+ await route.fulfill({ response: r, json: { ...b, sessions } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
44
+ await page.waitForTimeout(600);
45
+
46
+ const readAlphaBox = () => page.evaluate(() => {
47
+ // Find the alpha group <g data-group="alpha"> and read its <rect>.
48
+ const g = document.querySelector('g[data-group="alpha"]');
49
+ if (!g) return null;
50
+ const rect = g.querySelector('rect');
51
+ if (!rect) return null;
52
+ return {
53
+ strokeWidth: rect.getAttribute('stroke-width'),
54
+ stroke: rect.getAttribute('stroke'),
55
+ dashArray: rect.getAttribute('stroke-dasharray'),
56
+ fillOpacity: rect.getAttribute('fill-opacity'),
57
+ pinnedAttr: rect.getAttribute('data-group-box-pinned'),
58
+ };
59
+ });
60
+
61
+ const idle = await readAlphaBox();
62
+
63
+ // Hover a member of alpha (alpha1) → group becomes hovered.
64
+ await page.locator('g[data-node="alpha1"]').hover();
65
+ await page.waitForTimeout(300);
66
+ const hovered = await readAlphaBox();
67
+ await page.mouse.move(10, 10);
68
+ await page.waitForTimeout(300);
69
+
70
+ // Pin alpha via group label click.
71
+ await page.locator('g[data-group-label-hit="alpha"]').first().click({ force: true });
72
+ await page.mouse.move(10, 10);
73
+ await page.waitForTimeout(300);
74
+ const pinned = await readAlphaBox();
75
+
76
+ await browser.close();
77
+
78
+ const results = {
79
+ idle_stroke15: idle && idle.strokeWidth === '1.5',
80
+ idle_dashed: idle && (idle.dashArray || '').startsWith('6'),
81
+ idle_pinnedFalse: idle && idle.pinnedAttr === 'false',
82
+ hovered_stroke2: hovered && hovered.strokeWidth === '2',
83
+ hovered_solid: hovered && (hovered.dashArray === 'none' || hovered.dashArray === null),
84
+ hovered_pinnedFalse: hovered && hovered.pinnedAttr === 'false',
85
+ pinned_stroke3: pinned && pinned.strokeWidth === '3',
86
+ pinned_solid: pinned && (pinned.dashArray === 'none' || pinned.dashArray === null),
87
+ pinned_pinnedTrue: pinned && pinned.pinnedAttr === 'true',
88
+ pinned_strongerFill: pinned && hovered && parseFloat(pinned.fillOpacity) > parseFloat(hovered.fillOpacity),
89
+ };
90
+ const ok = Object.values(results).every(Boolean);
91
+ console.log(`${ok ? '✅' : '❌'} group-box pinned state:`, JSON.stringify(results),
92
+ `\n idle=`, idle,
93
+ `\n hovered=`, hovered,
94
+ `\n pinned=`, pinned);
95
+ process.exit(ok ? 0 : 1);