@sleep2agi/agent-network-dashboard 0.4.6-preview.32 → 0.4.6-preview.321
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 +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-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 +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- 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 +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- 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_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.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/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.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/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/07lsi~s-c0wvh.css +2 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/08s10wueoqydl.js +1 -0
- package/.next/static/chunks/0_l-j6k-ic81v.js +4 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0wlu_nyra0im0.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +126 -0
- package/app/components/TopoGraph.tsx +6091 -262
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/app/lib/vendorIdentity.ts +14 -3
- package/package.json +2 -2
- package/public/sleep2agi-logo.svg +11 -0
- package/public/vendors/claude.svg +11 -0
- package/public/vendors/minimax.svg +11 -0
- package/public/vendors/openai.svg +12 -0
- package/screenshots/r293-p0/after-full.png +0 -0
- package/screenshots/r293-p0/after-topo.png +0 -0
- package/screenshots/r293-p0/baseline-full.png +0 -0
- package/screenshots/r293-p0/baseline-topo.png +0 -0
- package/screenshots/r293-p0/logo-integrated-full.png +0 -0
- package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/p0-topo-screenshot.mjs +82 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-active-pulse-ease-test.mjs +120 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-brand-logo-transition-test.mjs +93 -0
- package/scripts/topo-brand-watermark-test.mjs +97 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
- package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
- package/scripts/topo-chip-empty-recede-test.mjs +116 -0
- package/scripts/topo-chip-focus-test.mjs +95 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
- package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
- package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -0
- package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
- package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
- package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
- package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
- package/scripts/topo-click-ripple-ease-test.mjs +94 -0
- package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
- package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
- package/scripts/topo-cmdk-pin-test.mjs +123 -0
- package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-corner-radius-unify-test.mjs +103 -0
- package/scripts/topo-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
- package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
- package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
- package/scripts/topo-edge-badge-hot-test.mjs +115 -0
- package/scripts/topo-edge-badge-hover-test.mjs +85 -0
- package/scripts/topo-edge-badge-lift-test.mjs +150 -0
- package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
- package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
- package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
- package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
- package/scripts/topo-edge-count-badge-test.mjs +86 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-fadein-test.mjs +106 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
- package/scripts/topo-empty-state-recenter-test.mjs +107 -0
- package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
- package/scripts/topo-fullscreen-active-test.mjs +115 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +6 -1
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pin-test.mjs +119 -0
- package/scripts/topo-group-pip-fadein-test.mjs +100 -0
- package/scripts/topo-group-pip-tabular-test.mjs +95 -0
- package/scripts/topo-group-status-pips-test.mjs +103 -0
- package/scripts/topo-groupbox-fadein-test.mjs +95 -0
- package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
- package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
- package/scripts/topo-groupbox-live-test.mjs +100 -0
- package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r118-test.mjs +69 -0
- package/scripts/topo-help-overlay-r123-test.mjs +69 -0
- package/scripts/topo-help-overlay-r134-test.mjs +74 -0
- package/scripts/topo-help-overlay-r137-test.mjs +81 -0
- package/scripts/topo-help-overlay-r141-test.mjs +86 -0
- package/scripts/topo-help-overlay-r153-test.mjs +85 -0
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
- package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
- package/scripts/topo-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-digit-scale-test.mjs +129 -0
- package/scripts/topo-hub-fadein-test.mjs +88 -0
- package/scripts/topo-hub-hover-lift-test.mjs +101 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -0
- package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
- package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
- package/scripts/topo-kicker-color-test.mjs +93 -0
- package/scripts/topo-kicker-tracking-test.mjs +101 -0
- package/scripts/topo-kicker-weight-test.mjs +98 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-legend-compress-test.mjs +107 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -0
- package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
- package/scripts/topo-legend-count-tabular-test.mjs +87 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
- package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
- package/scripts/topo-legend-offline-label-simplify-test.mjs +85 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -0
- package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
- package/scripts/topo-legend-pin-test.mjs +128 -0
- package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
- package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-legend-row-title-test.mjs +102 -0
- package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
- package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
- package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-panel-fadein-test.mjs +102 -0
- package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-panel-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
- package/scripts/topo-panel-title-tracking-test.mjs +78 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
- package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
- package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
- package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
- package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
- package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
- package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-fadein-test.mjs +107 -0
- package/scripts/topo-recent-row-freshness-test.mjs +102 -0
- package/scripts/topo-recent-row-hot-test.mjs +110 -0
- package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
- package/scripts/topo-recent-row-pin-test.mjs +126 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-title-test.mjs +112 -0
- package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
- package/scripts/topo-recent-row-ts-test.mjs +95 -0
- package/scripts/topo-recent-row-typography-test.mjs +119 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spokes-retired-test.mjs +93 -0
- package/scripts/topo-starfield-density-test.mjs +89 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -0
- package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
- package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
- package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
- package/scripts/topo-title-tracking-test.mjs +98 -0
- package/scripts/topo-titleblock-gap-test.mjs +86 -0
- package/scripts/topo-titleblock-margin-test.mjs +83 -0
- package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
- package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-view-smooth-test.mjs +131 -0
- package/scripts/topo-watermark-tracking-test.mjs +99 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/08b1xx89c0ts9.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_8snedeata9k.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0h_~v6f9o~_in.js +0 -1
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0pl7l_.2im.j0.js +0 -7
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- /package/.next/static/{QkCLPQNNPLHlzKVvmAInn → 95X2ICTd2SB5ohx7R2QN2}/_buildManifest.js +0 -0
- /package/.next/static/{QkCLPQNNPLHlzKVvmAInn → 95X2ICTd2SB5ohx7R2QN2}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{QkCLPQNNPLHlzKVvmAInn → 95X2ICTd2SB5ohx7R2QN2}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 55 verification: hovering a legend status row dims nodes whose
|
|
2
|
+
* status doesn't match.
|
|
3
|
+
*
|
|
4
|
+
* Sessions (mix of statuses):
|
|
5
|
+
* wkr — working (status=working)
|
|
6
|
+
* idl1 — idle online (status=idle)
|
|
7
|
+
* idl2 — idle online (status=idle)
|
|
8
|
+
* off — offline (status=offline, last_seen old)
|
|
9
|
+
*
|
|
10
|
+
* Hover the "working node" row → wkr stays bright (~1.0), the rest dim
|
|
11
|
+
* (~0.28). Hover "online idle" → idl1/idl2 stay, wkr+off dim. Hover
|
|
12
|
+
* "offline / no SSE" → off stays, the three online dim. Release →
|
|
13
|
+
* all back to base.
|
|
14
|
+
*/
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
|
|
21
|
+
// Dashboard chrome pushes the SVG ~830 px down at width 1400, so the
|
|
22
|
+
// legend's offline row sits at screen y≈900 — below a 900-px viewport.
|
|
23
|
+
// 1500 px tall gives a comfortable margin for all three rows.
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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 {
|
|
28
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
29
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
30
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
31
|
+
} catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
const stale = new Date(Date.now() - 30 * 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 sessions = [
|
|
41
|
+
{ alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
42
|
+
{ alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
43
|
+
{ alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
44
|
+
{ alias: 'off', status: 'offline', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
45
|
+
];
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(500);
|
|
54
|
+
|
|
55
|
+
const opacities = () => page.evaluate(() => {
|
|
56
|
+
const o = {};
|
|
57
|
+
for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
|
|
58
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
59
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
60
|
+
}
|
|
61
|
+
return o;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const before = await opacities();
|
|
65
|
+
|
|
66
|
+
async function hoverRow(key) {
|
|
67
|
+
const target = await page.evaluate((k) => {
|
|
68
|
+
const g = document.querySelector(`g[data-legend-status="${k}"]`);
|
|
69
|
+
if (!g) return null;
|
|
70
|
+
const r = g.getBoundingClientRect();
|
|
71
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
72
|
+
}, key);
|
|
73
|
+
if (!target) throw new Error(`legend row ${key} not found`);
|
|
74
|
+
// jiggle from a neutral spot, then to the target
|
|
75
|
+
await page.mouse.move(10, 10);
|
|
76
|
+
await page.mouse.move(target.x, target.y);
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
return await opacities();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const onWorking = await hoverRow('working');
|
|
82
|
+
const onIdle = await hoverRow('idle');
|
|
83
|
+
const onOffline = await hoverRow('offline');
|
|
84
|
+
await page.mouse.move(10, 10);
|
|
85
|
+
await page.waitForTimeout(300);
|
|
86
|
+
const released = await opacities();
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const dim = (v) => v != null && v < 0.4;
|
|
91
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
92
|
+
const results = {
|
|
93
|
+
before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2),
|
|
94
|
+
working_keepsWorking: bright(onWorking.wkr),
|
|
95
|
+
working_dimsIdle: dim(onWorking.idl1) && dim(onWorking.idl2),
|
|
96
|
+
working_dimsOffline: dim(onWorking.off),
|
|
97
|
+
idle_keepsIdle: bright(onIdle.idl1) && bright(onIdle.idl2),
|
|
98
|
+
idle_dimsWorking: dim(onIdle.wkr),
|
|
99
|
+
idle_dimsOffline: dim(onIdle.off),
|
|
100
|
+
// Offline base opacity is 0.6 (R17), so "kept" means >= 0.55 which the
|
|
101
|
+
// bright() helper already covers — anything in [0.55, 1] counts.
|
|
102
|
+
offline_keepsOffline: bright(onOffline.off),
|
|
103
|
+
offline_dimsOnline: dim(onOffline.wkr) && dim(onOffline.idl1) && dim(onOffline.idl2),
|
|
104
|
+
released_restoresAll: bright(released.wkr) && bright(released.idl1) && bright(released.idl2),
|
|
105
|
+
};
|
|
106
|
+
const ok = Object.values(results).every(Boolean);
|
|
107
|
+
console.log(`${ok ? '✅' : '❌'} legend status hover:`, JSON.stringify(results),
|
|
108
|
+
`\n before=`, before,
|
|
109
|
+
`\n onWorking=`, onWorking,
|
|
110
|
+
`\n onIdle=`, onIdle,
|
|
111
|
+
`\n onOffline=`, onOffline,
|
|
112
|
+
`\n released=`, released);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Round 269 verification: legend offline-row label " / " → " · "
|
|
2
|
+
* delimiter unification — closes R138 sweep.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R269 the legend's offline row read "offline / no SSE" (ASCII
|
|
5
|
+
* forward-slash separator). R138 already swept the recent-signal
|
|
6
|
+
* row separators to " · " (typographic middot) along with filter
|
|
7
|
+
* pills, node tooltips, edge badges, and active-links tooltip. The
|
|
8
|
+
* legend offline label was the last hardcoded " / " in TopoGraph.
|
|
9
|
+
*
|
|
10
|
+
* R269 changes the label string from "offline / no SSE" to
|
|
11
|
+
* "offline · no SSE" — same monospace cell width (no layout shift),
|
|
12
|
+
* matches the dashboard-wide typographic delimiter convention.
|
|
13
|
+
*
|
|
14
|
+
* Test scope:
|
|
15
|
+
* 1. Legend offline row label text contains the typographic middot.
|
|
16
|
+
* 2. Label DOES NOT contain " / " (the ASCII separator).
|
|
17
|
+
* 3. Working + idle row labels unchanged (regression: they never
|
|
18
|
+
* used " / ").
|
|
19
|
+
* 4. R257 legend header symmetric inset intact (legend count x === 211).
|
|
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, status = 'working') => ({
|
|
37
|
+
alias, status, 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'), mk('beta'), mk('gamma'), mk('delta'),
|
|
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
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
51
|
+
await page.waitForSelector('[data-legend-row-label="offline"]', { timeout: 10000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-row-label="working"]', { timeout: 10000 });
|
|
53
|
+
await page.waitForSelector('[data-legend-row-label="idle"]', { timeout: 10000 });
|
|
54
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 10000 });
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
|
|
57
|
+
const probe = await page.evaluate(() => {
|
|
58
|
+
const offline = document.querySelector('[data-legend-row-label="offline"]');
|
|
59
|
+
const working = document.querySelector('[data-legend-row-label="working"]');
|
|
60
|
+
const idle = document.querySelector('[data-legend-row-label="idle"]');
|
|
61
|
+
const count = document.querySelector('[data-legend-panel-count]');
|
|
62
|
+
return {
|
|
63
|
+
offlineText: offline ? offline.textContent : null,
|
|
64
|
+
workingText: working ? working.textContent : null,
|
|
65
|
+
idleText: idle ? idle.textContent : null,
|
|
66
|
+
countX: count ? +count.getAttribute('x') : null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
offline_uses_middot: probe.offlineText === 'offline · no SSE',
|
|
73
|
+
offline_no_ascii_slash: probe.offlineText != null && !probe.offlineText.includes(' / '),
|
|
74
|
+
working_text_unchanged: probe.workingText === 'working node',
|
|
75
|
+
idle_text_unchanged: probe.idleText === 'online idle',
|
|
76
|
+
r257_legend_count_x_211: probe.countX === 211,
|
|
77
|
+
};
|
|
78
|
+
const ok = Object.values(results).every(Boolean);
|
|
79
|
+
console.log(`${ok ? '✅' : '❌'} legend offline delimiter:`, JSON.stringify(results),
|
|
80
|
+
'\n offline:', JSON.stringify(probe.offlineText),
|
|
81
|
+
'\n working:', JSON.stringify(probe.workingText),
|
|
82
|
+
'\n idle: ', JSON.stringify(probe.idleText),
|
|
83
|
+
'\n count x:', probe.countX);
|
|
84
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 307 verification: legend offline row label drops the
|
|
2
|
+
* '· no SSE' qualifier. 'offline · no SSE' (16 chars) → 'offline'
|
|
3
|
+
* (7 chars). Visual already communicates the disconnected state
|
|
4
|
+
* via status ring dashed pattern + gray fill + gray swatch.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - [data-legend-row-label='offline'] text content === 'offline'.
|
|
8
|
+
* - Does NOT contain 'no SSE' substring.
|
|
9
|
+
* - Working row label still 'working node'.
|
|
10
|
+
* - Idle row label still 'online idle'.
|
|
11
|
+
* - R306 focus-ring-1 + R305 alias chat-target attr + R304/R302/
|
|
12
|
+
* R301/R294 regressions intact.
|
|
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, model, status) => ({
|
|
30
|
+
alias, status, model, 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', 'claude-opus-4', 'working'),
|
|
36
|
+
mk('beta', 'gpt-4o', 'idle'),
|
|
37
|
+
mk('gamma', 'claude-sonnet-4', 'offline'),
|
|
38
|
+
] } });
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
41
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
42
|
+
|
|
43
|
+
const page = await ctx.newPage();
|
|
44
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
45
|
+
await page.waitForSelector('[data-legend-row-label="offline"]', { timeout: 15000 });
|
|
46
|
+
await page.waitForTimeout(300);
|
|
47
|
+
|
|
48
|
+
const probe = await page.evaluate(() => {
|
|
49
|
+
const sel = (s) => document.querySelector(s);
|
|
50
|
+
const txt = (el) => (el?.textContent || '').trim();
|
|
51
|
+
const offlineLabel = sel('[data-legend-row-label="offline"]');
|
|
52
|
+
const workingLabel = sel('[data-legend-row-label="working"]');
|
|
53
|
+
const idleLabel = sel('[data-legend-row-label="idle"]');
|
|
54
|
+
const layoutRing = sel('[data-topo-chrome-layout="ring"]');
|
|
55
|
+
const alias = sel('[data-node-alias-text]');
|
|
56
|
+
const subhint = sel('[data-recent-signal-empty-hint]');
|
|
57
|
+
return {
|
|
58
|
+
offlineText: txt(offlineLabel),
|
|
59
|
+
workingText: txt(workingLabel),
|
|
60
|
+
idleText: txt(idleLabel),
|
|
61
|
+
layoutRingCls: layoutRing?.className ?? '',
|
|
62
|
+
aliasChatTargetAttr: alias?.hasAttribute('data-node-alias-chat-target') ?? false,
|
|
63
|
+
subhintLs: subhint?.getAttribute('letter-spacing') ?? null,
|
|
64
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
await browser.close();
|
|
68
|
+
|
|
69
|
+
const results = {
|
|
70
|
+
offline_label_simplified: probe.offlineText === 'offline',
|
|
71
|
+
offline_label_no_sse_dropped: !/no SSE/i.test(probe.offlineText),
|
|
72
|
+
working_label_kept: probe.workingText === 'working node',
|
|
73
|
+
idle_label_kept: probe.idleText === 'online idle',
|
|
74
|
+
r306_layout_ring_1_kept: probe.layoutRingCls.includes('focus-visible:ring-1'),
|
|
75
|
+
r306_layout_no_ring_2: !probe.layoutRingCls.includes('focus-visible:ring-2'),
|
|
76
|
+
r305_alias_chat_target_attr: probe.aliasChatTargetAttr,
|
|
77
|
+
r304_subhint_ls_0_15: probe.subhintLs === '0.15',
|
|
78
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
79
|
+
};
|
|
80
|
+
const ok = Object.values(results).every(Boolean);
|
|
81
|
+
console.log(`${ok ? '✅' : '❌'} legend offline label simplify:`, JSON.stringify(results),
|
|
82
|
+
'\n offline:', JSON.stringify(probe.offlineText),
|
|
83
|
+
'\n working:', JSON.stringify(probe.workingText),
|
|
84
|
+
'\n idle:', JSON.stringify(probe.idleText));
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* Round 106 verification: legend panel gains a header matching the
|
|
2
|
+
* recent-signal panel's "recent signal · N flows" format. Same
|
|
3
|
+
* font/position vocabulary so the two side panels feel paired. The
|
|
4
|
+
* fleet count is right-aligned at x=215 in the accent colour.
|
|
5
|
+
*
|
|
6
|
+
* Sanity-checks:
|
|
7
|
+
* - "legend" title text exists in the legend panel
|
|
8
|
+
* - "N node(s)" count matches sessions.length
|
|
9
|
+
* - Singular vs plural ("1 node" vs "5 nodes") correct
|
|
10
|
+
* - Existing rows still render (hover affordance preserved)
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
|
|
19
|
+
const probe = async (count) => {
|
|
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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
24
|
+
});
|
|
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 sessions = [];
|
|
30
|
+
for (let i = 0; i < count; i++) {
|
|
31
|
+
sessions.push({
|
|
32
|
+
alias: `n${i}`, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
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((n) => document.querySelectorAll('g[data-node]').length === n, count, { timeout: 30000 });
|
|
44
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 10000 });
|
|
45
|
+
await page.waitForTimeout(400);
|
|
46
|
+
|
|
47
|
+
const data = await page.evaluate(() => {
|
|
48
|
+
const headerCount = document.querySelector('[data-legend-panel-count]')?.textContent;
|
|
49
|
+
const headerTitle = (() => {
|
|
50
|
+
// Find a <text> with "legend" content in the legend panel <g>
|
|
51
|
+
const panel = document.querySelector('[data-topo-panel-elevation="legend"]')?.parentElement;
|
|
52
|
+
if (!panel) return null;
|
|
53
|
+
for (const t of panel.querySelectorAll('text')) {
|
|
54
|
+
if (t.textContent === 'legend') return t.textContent;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
})();
|
|
58
|
+
const legendRows = document.querySelectorAll('[data-legend-status]').length;
|
|
59
|
+
return { headerCount, headerTitle, legendRows };
|
|
60
|
+
});
|
|
61
|
+
await ctx.close();
|
|
62
|
+
return data;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const single = await probe(1);
|
|
66
|
+
const many = await probe(5);
|
|
67
|
+
await browser.close();
|
|
68
|
+
|
|
69
|
+
const results = {
|
|
70
|
+
single_hasTitle: single.headerTitle === 'legend',
|
|
71
|
+
single_count1Node: single.headerCount === '1 node',
|
|
72
|
+
many_hasTitle: many.headerTitle === 'legend',
|
|
73
|
+
many_count5Nodes: many.headerCount === '5 nodes',
|
|
74
|
+
many_rowsStillRender: many.legendRows >= 1, // depends on which states are present
|
|
75
|
+
// Sanity: singular vs plural
|
|
76
|
+
singular_correct: single.headerCount?.endsWith(' node'),
|
|
77
|
+
plural_correct: many.headerCount?.endsWith(' nodes'),
|
|
78
|
+
};
|
|
79
|
+
const ok = Object.values(results).every(Boolean);
|
|
80
|
+
console.log(`${ok ? '✅' : '❌'} legend panel header:`, JSON.stringify(results),
|
|
81
|
+
`\n single (1 node)=`, single,
|
|
82
|
+
`\n many (5 nodes)=`, many);
|
|
83
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Round 181 verification: legend row pinned-state ring (R61) is
|
|
2
|
+
* now always-mounted with opacity-gated visibility + 150ms
|
|
3
|
+
* transition — smooth fade in/out on pin / unpin.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R181 the ring mounted/unmounted conditionally, snapping
|
|
6
|
+
* on every state change. Same vocabulary as R165/R180 smooth-
|
|
7
|
+
* pin-mirror family, applied at the legend-row scope.
|
|
8
|
+
*
|
|
9
|
+
* Test:
|
|
10
|
+
* 1. Idle: ring exists in DOM but opacity=0
|
|
11
|
+
* 2. Click 'working' legend row → ring for working = opacity=1
|
|
12
|
+
* 3. Other rings (idle / offline) stay opacity=0
|
|
13
|
+
* 4. Re-click 'working' → unpin → opacity returns to 0
|
|
14
|
+
* 5. All rings carry 'opacity 150ms ease-out' transition
|
|
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
|
+
|
|
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
|
+
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
const stale = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias, status, last) => ({
|
|
35
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: last, updated_at: last, last_seen_at: last,
|
|
38
|
+
});
|
|
39
|
+
// Mix of statuses so all three legend rows render
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('w1', 'working', fresh), mk('w2', 'working', fresh),
|
|
42
|
+
mk('i1', 'idle', fresh),
|
|
43
|
+
mk('o1', 'offline', stale),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.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.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-pin-ring]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
const probe = () => page.evaluate(() => {
|
|
56
|
+
const grab = (k) => {
|
|
57
|
+
const r = document.querySelector(`[data-legend-pin-ring="${k}"]`);
|
|
58
|
+
return r ? {
|
|
59
|
+
opacity: parseFloat(r.getAttribute('opacity') || ''),
|
|
60
|
+
pinnedAttr: r.getAttribute('data-legend-pin-ring-pinned'),
|
|
61
|
+
transition: r.style.transition || getComputedStyle(r).transition,
|
|
62
|
+
} : null;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
working: grab('working'),
|
|
66
|
+
idle: grab('idle'),
|
|
67
|
+
offline: grab('offline'),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const idle = await probe();
|
|
72
|
+
|
|
73
|
+
// Pin 'working' status by clicking the legend row
|
|
74
|
+
await page.locator('[data-legend-status="working"]').click();
|
|
75
|
+
await page.waitForTimeout(250);
|
|
76
|
+
const afterPin = await probe();
|
|
77
|
+
|
|
78
|
+
// Re-click to unpin
|
|
79
|
+
await page.locator('[data-legend-status="working"]').click();
|
|
80
|
+
await page.waitForTimeout(250);
|
|
81
|
+
const afterUnpin = await probe();
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const hasTransition = (s) =>
|
|
86
|
+
(s?.transition || '').includes('opacity 150ms') ||
|
|
87
|
+
/opacity\s+0\.15s|opacity\s+150ms/.test(s?.transition || '');
|
|
88
|
+
|
|
89
|
+
const results = {
|
|
90
|
+
// Idle (no pin)
|
|
91
|
+
three_rings_present: idle.working !== null && idle.idle !== null && idle.offline !== null,
|
|
92
|
+
idle_working_opacity_0: idle.working?.opacity === 0,
|
|
93
|
+
idle_idle_opacity_0: idle.idle?.opacity === 0,
|
|
94
|
+
idle_offline_opacity_0: idle.offline?.opacity === 0,
|
|
95
|
+
all_have_transition: hasTransition(idle.working) && hasTransition(idle.idle) && hasTransition(idle.offline),
|
|
96
|
+
|
|
97
|
+
// After pin working
|
|
98
|
+
pin_working_opacity_1: afterPin.working?.opacity === 1,
|
|
99
|
+
pin_working_pinned_attr: afterPin.working?.pinnedAttr === 'true',
|
|
100
|
+
pin_idle_stays_0: afterPin.idle?.opacity === 0,
|
|
101
|
+
pin_offline_stays_0: afterPin.offline?.opacity === 0,
|
|
102
|
+
|
|
103
|
+
// After unpin
|
|
104
|
+
unpin_working_opacity_0: afterUnpin.working?.opacity === 0,
|
|
105
|
+
unpin_working_pinned_attr: afterUnpin.working?.pinnedAttr === 'false',
|
|
106
|
+
};
|
|
107
|
+
const ok = Object.values(results).every(Boolean);
|
|
108
|
+
console.log(`${ok ? '✅' : '❌'} legend pin-ring fade:`, JSON.stringify(results),
|
|
109
|
+
`\n idle =`, idle,
|
|
110
|
+
`\n pinned =`, afterPin,
|
|
111
|
+
`\n unpinned =`, afterUnpin);
|
|
112
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* Round 61 verification: legend rows also pin status filter on click.
|
|
2
|
+
* - Hovering still works (R55, transient release).
|
|
3
|
+
* - Clicking a row toggles pinnedStatus same as R60 pressure segments.
|
|
4
|
+
* - Pinned row's swatch gets an outer ring at r=8 (concentric, row colour).
|
|
5
|
+
* - Click same row again → release.
|
|
6
|
+
* - Sticky: pin survives mouse-move-away (vs R55 hover which clears).
|
|
7
|
+
* - The R60 pressure-segment pin and this share `pinnedStatus`, so
|
|
8
|
+
* clicking the legend then clicking the pressure-segment for a
|
|
9
|
+
* different status replaces (not stacks) the filter — verified by
|
|
10
|
+
* chain: legend.working → pressure.idle → only idle stays bright.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
23
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
24
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
} catch {}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const sessions = [
|
|
34
|
+
{ alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
35
|
+
{ alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
36
|
+
{ alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
37
|
+
];
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
41
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
45
|
+
await page.waitForTimeout(500);
|
|
46
|
+
|
|
47
|
+
const opacities = () => page.evaluate(() => {
|
|
48
|
+
const o = {};
|
|
49
|
+
for (const a of ['wkr', 'idl1', 'idl2']) {
|
|
50
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
51
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
52
|
+
}
|
|
53
|
+
return o;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const before = await opacities();
|
|
57
|
+
|
|
58
|
+
// Click the WORKING legend row.
|
|
59
|
+
const workingLegend = page.locator('g[data-legend-status="working"]').first();
|
|
60
|
+
const workingExists = await workingLegend.count() > 0;
|
|
61
|
+
if (!workingExists) { console.log('❌ legend row[working] not found'); process.exit(1); }
|
|
62
|
+
await workingLegend.click({ force: true });
|
|
63
|
+
await page.waitForTimeout(250);
|
|
64
|
+
const afterPin = await opacities();
|
|
65
|
+
|
|
66
|
+
// Pin survives mouse move.
|
|
67
|
+
await page.mouse.move(10, 10);
|
|
68
|
+
await page.waitForTimeout(250);
|
|
69
|
+
const afterMouseMove = await opacities();
|
|
70
|
+
|
|
71
|
+
// Pinned row should carry an aria-pressed=true.
|
|
72
|
+
const pinnedAria = await page.evaluate(() => {
|
|
73
|
+
const g = document.querySelector('g[data-legend-status="working"]');
|
|
74
|
+
return g?.getAttribute('aria-pressed');
|
|
75
|
+
});
|
|
76
|
+
// And a concentric r=8 ring exists inside it.
|
|
77
|
+
const pinnedRingExists = await page.evaluate(() => {
|
|
78
|
+
const g = document.querySelector('g[data-legend-status="working"]');
|
|
79
|
+
if (!g) return false;
|
|
80
|
+
return [...g.querySelectorAll('circle')].some(c => c.getAttribute('r') === '8');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Switch pin via pressure segment (idle) — should REPLACE, not stack.
|
|
84
|
+
const idleSeg = page.locator('[data-pressure-seg="idle"]').first();
|
|
85
|
+
await idleSeg.click({ force: true });
|
|
86
|
+
await page.waitForTimeout(250);
|
|
87
|
+
const afterSwitch = await opacities();
|
|
88
|
+
|
|
89
|
+
// And the legend pin should release (concentric ring gone from working).
|
|
90
|
+
const workingRingGone = await page.evaluate(() => {
|
|
91
|
+
const g = document.querySelector('g[data-legend-status="working"]');
|
|
92
|
+
if (!g) return true;
|
|
93
|
+
return ![...g.querySelectorAll('circle')].some(c => c.getAttribute('r') === '8');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Click idle legend again to release pin. The click ALSO triggers a
|
|
97
|
+
// mouseenter that sets hoveredStatus='idle', which would keep the
|
|
98
|
+
// filter active (activeStatus = hoveredStatus ?? pinnedStatus). Move
|
|
99
|
+
// the mouse away to clear the hover so we observe the pin-only state.
|
|
100
|
+
await page.locator('g[data-legend-status="idle"]').first().click({ force: true });
|
|
101
|
+
await page.mouse.move(10, 10);
|
|
102
|
+
await page.waitForTimeout(250);
|
|
103
|
+
const afterRelease = await opacities();
|
|
104
|
+
|
|
105
|
+
await browser.close();
|
|
106
|
+
|
|
107
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
108
|
+
const dim = (v) => v != null && v < 0.4;
|
|
109
|
+
const results = {
|
|
110
|
+
before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2),
|
|
111
|
+
legendPin_keepsWorking: bright(afterPin.wkr),
|
|
112
|
+
legendPin_dimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
|
|
113
|
+
legendPin_survivesMouse: bright(afterMouseMove.wkr) && dim(afterMouseMove.idl1),
|
|
114
|
+
legendPin_ariaPressed: pinnedAria === 'true',
|
|
115
|
+
legendPin_ringRendered: pinnedRingExists,
|
|
116
|
+
pressureSwitch_replaces: dim(afterSwitch.wkr) && bright(afterSwitch.idl1) && bright(afterSwitch.idl2),
|
|
117
|
+
pressureSwitch_clearsLegendRing: workingRingGone,
|
|
118
|
+
release_restoresAll: bright(afterRelease.wkr) && bright(afterRelease.idl1),
|
|
119
|
+
};
|
|
120
|
+
const ok = Object.values(results).every(Boolean);
|
|
121
|
+
console.log(`${ok ? '✅' : '❌'} legend pin:`, JSON.stringify(results),
|
|
122
|
+
`\n before=`, before,
|
|
123
|
+
`\n afterPin=`, afterPin,
|
|
124
|
+
`\n afterMouseMove=`, afterMouseMove,
|
|
125
|
+
`\n afterSwitch=`, afterSwitch,
|
|
126
|
+
`\n afterRelease=`, afterRelease,
|
|
127
|
+
`\n pinnedAria=${pinnedAria} ringRendered=${pinnedRingExists} workingRingGone=${workingRingGone}`);
|
|
128
|
+
process.exit(ok ? 0 : 1);
|