@sleep2agi/agent-network-dashboard 0.4.6-preview.31 → 0.4.6-preview.310
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/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0o40uf-rn28q6.js +1 -0
- package/.next/static/chunks/0q3hr1~viwzkt.js +4 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/0~ks9b48lw8hx.css +2 -0
- package/.next/static/chunks/13lvf-h-j8bmc.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 +5927 -258
- 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-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-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-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-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-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-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-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-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-pulse-rate-test.mjs +71 -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-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-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/01u5zo8b7r_r3.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0fqit.b4j_6rz.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/0qcfcgdthhos6.js +0 -1
- 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/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_buildManifest.js +0 -0
- /package/.next/static/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* Round 215 verification: edge midpoint count badge always-mounts;
|
|
2
|
+
* visibility crossfades via wrapper <g> opacity instead of mount/
|
|
3
|
+
* unmount on link.count >= 3 threshold. Closes the canvas-edge
|
|
4
|
+
* surface of the always-mount-opacity-gate family.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R215 a flow's first 2 → 3 message snap-popped the badge in
|
|
7
|
+
* one frame, and a hot flow tapering 3 → 2 vanished in one frame.
|
|
8
|
+
* R215 always-mounts and opacity-gates, giving 300ms ease through
|
|
9
|
+
* the threshold crossing.
|
|
10
|
+
*
|
|
11
|
+
* Test (cyber dark, mock 3 flows below + at + above threshold):
|
|
12
|
+
* 1. flow A: count=2 (below threshold) → badge in DOM but
|
|
13
|
+
* visible='false', opacity 0, pointerEvents 'none',
|
|
14
|
+
* aria-hidden='true', role unset, tabIndex=-1
|
|
15
|
+
* 2. flow B: count=3 (at threshold) → badge visible='true',
|
|
16
|
+
* opacity > 0, pointerEvents 'all', role='button', tabIndex=0
|
|
17
|
+
* 3. flow C: count=12 (hot) → badge visible='true',
|
|
18
|
+
* hot='true', opacity > 0, all interactive
|
|
19
|
+
* 4. Every wrapper has inline transition: 'opacity 300ms ease-out'
|
|
20
|
+
* 5. text content always matches link.count (even the hidden one)
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
|
|
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
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias) => ({
|
|
40
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const msgs = [];
|
|
51
|
+
// flow A: alpha → beta · count=2 (below threshold, badge hidden)
|
|
52
|
+
for (let i = 0; i < 2; i++) {
|
|
53
|
+
msgs.push({ id: `A${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
54
|
+
network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString() });
|
|
55
|
+
}
|
|
56
|
+
// flow B: alpha → gamma · count=3 (at threshold, badge visible)
|
|
57
|
+
for (let i = 0; i < 3; i++) {
|
|
58
|
+
msgs.push({ id: `B${i}`, from_alias: 'alpha', to_alias: 'gamma', content: 'hi',
|
|
59
|
+
network_id: 'default', created_at: new Date(now - (5000 + i * 50)).toISOString() });
|
|
60
|
+
}
|
|
61
|
+
// flow C: alpha → delta · count=12 (hot)
|
|
62
|
+
for (let i = 0; i < 12; i++) {
|
|
63
|
+
msgs.push({ id: `C${i}`, from_alias: 'alpha', to_alias: 'delta', content: 'hi',
|
|
64
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 50)).toISOString() });
|
|
65
|
+
}
|
|
66
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: msgs } }));
|
|
67
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
68
|
+
|
|
69
|
+
const page = await ctx.newPage();
|
|
70
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
71
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
72
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000, state: 'attached' });
|
|
73
|
+
await page.waitForTimeout(400);
|
|
74
|
+
|
|
75
|
+
const probe = await page.evaluate(() => {
|
|
76
|
+
return [...document.querySelectorAll('[data-edge-count-badge]')].map(el => {
|
|
77
|
+
const cs = getComputedStyle(el);
|
|
78
|
+
const txt = el.querySelector('text')?.textContent;
|
|
79
|
+
return {
|
|
80
|
+
key: el.getAttribute('data-edge-count-badge'),
|
|
81
|
+
visible: el.getAttribute('data-edge-count-badge-visible'),
|
|
82
|
+
hot: el.getAttribute('data-edge-count-badge-hot'),
|
|
83
|
+
role: el.getAttribute('role'),
|
|
84
|
+
tabIndex: el.getAttribute('tabindex'),
|
|
85
|
+
ariaHidden: el.getAttribute('aria-hidden'),
|
|
86
|
+
opacity: parseFloat(el.getAttribute('opacity') || ''),
|
|
87
|
+
computedPointer: cs.pointerEvents,
|
|
88
|
+
transition: el.style.transition,
|
|
89
|
+
text: txt,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await browser.close();
|
|
95
|
+
|
|
96
|
+
const flowA = probe.find(p => p.key === 'alpha->beta'); // count=2
|
|
97
|
+
const flowB = probe.find(p => p.key === 'alpha->gamma'); // count=3
|
|
98
|
+
const flowC = probe.find(p => p.key === 'alpha->delta'); // count=12
|
|
99
|
+
|
|
100
|
+
const results = {
|
|
101
|
+
// All 3 wrappers present in DOM (always-mount)
|
|
102
|
+
three_badges_in_dom: probe.length === 3,
|
|
103
|
+
flowA_present: !!flowA,
|
|
104
|
+
flowB_present: !!flowB,
|
|
105
|
+
flowC_present: !!flowC,
|
|
106
|
+
|
|
107
|
+
// flow A (count=2, hidden)
|
|
108
|
+
A_visible_false: flowA?.visible === 'false',
|
|
109
|
+
A_opacity_0: flowA?.opacity === 0,
|
|
110
|
+
A_pointer_none: flowA?.computedPointer === 'none',
|
|
111
|
+
A_aria_hidden: flowA?.ariaHidden === 'true',
|
|
112
|
+
A_role_unset: flowA?.role === null || flowA?.role === '',
|
|
113
|
+
A_text_2: flowA?.text === '2',
|
|
114
|
+
|
|
115
|
+
// flow B (count=3, just visible)
|
|
116
|
+
B_visible_true: flowB?.visible === 'true',
|
|
117
|
+
B_opacity_positive: (flowB?.opacity ?? 0) > 0,
|
|
118
|
+
B_pointer_all: flowB?.computedPointer === 'all',
|
|
119
|
+
B_role_button: flowB?.role === 'button',
|
|
120
|
+
B_text_3: flowB?.text === '3',
|
|
121
|
+
B_not_hot: flowB?.hot === 'false',
|
|
122
|
+
|
|
123
|
+
// flow C (count=12, hot)
|
|
124
|
+
C_visible_true: flowC?.visible === 'true',
|
|
125
|
+
C_hot_true: flowC?.hot === 'true',
|
|
126
|
+
C_opacity_positive: (flowC?.opacity ?? 0) > 0,
|
|
127
|
+
C_text_12: flowC?.text === '12',
|
|
128
|
+
|
|
129
|
+
// All have transition with opacity 300ms (even hidden ones — they
|
|
130
|
+
// need the transition for when count crosses back into threshold)
|
|
131
|
+
all_have_transition: probe.every(p => /opacity\s+(?:300ms|0\.3s)/.test(p.transition || '')),
|
|
132
|
+
};
|
|
133
|
+
const ok = Object.values(results).every(Boolean);
|
|
134
|
+
console.log(`${ok ? '✅' : '❌'} edge badge crossfade:`, JSON.stringify(results),
|
|
135
|
+
`\n flowA (count=2 hidden):`, flowA,
|
|
136
|
+
`\n flowB (count=3 visible):`, flowB,
|
|
137
|
+
`\n flowC (count=12 hot):`, flowC);
|
|
138
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Round 126 verification: hot-edge accent on the midpoint count
|
|
2
|
+
* badge. The edge stroke width (line 2344 of TopoGraph.tsx) scales
|
|
3
|
+
* with count but clamps at 7px — so count=5 and count=50 look
|
|
4
|
+
* identical at the line. R100 introduced the badge for high-traffic
|
|
5
|
+
* lanes, but the badge itself stayed the same colour regardless.
|
|
6
|
+
*
|
|
7
|
+
* R126 buckets count ≥ 10 as "hot" and flips the badge stroke to
|
|
8
|
+
* amber (#d97706 light, #fbbf24 dark) + thickens to width 2. Pin
|
|
9
|
+
* still wins (uses legendHeadline) so pinned-hot reads as locked
|
|
10
|
+
* via the badge text + edge brightness, not via a third stroke
|
|
11
|
+
* colour. Reuses R125's amber convention — "amber draws the eye".
|
|
12
|
+
*
|
|
13
|
+
* Fleet:
|
|
14
|
+
* alpha (working) — pings beta with 12 msgs (hot)
|
|
15
|
+
* beta (working) — pings gamma with 4 msgs (warm, badge but not hot)
|
|
16
|
+
* gamma (idle)
|
|
17
|
+
*
|
|
18
|
+
* Path:
|
|
19
|
+
* 1. alpha→beta: badge hot=true, stroke matches amber rgb
|
|
20
|
+
* 2. beta→gamma: badge hot=false, stroke matches flowEdge (not amber)
|
|
21
|
+
* 3. Click alpha→beta to pin; pin still wins (stroke = legendHeadline,
|
|
22
|
+
* data-edge-count-badge-pinned=true, hot=true is still flagged
|
|
23
|
+
* via the attribute but the visible stroke is the pin colour)
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
39
|
+
const r = await route.fetch();
|
|
40
|
+
const b = await r.json();
|
|
41
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
42
|
+
const mk = (alias, status) => ({
|
|
43
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
48
|
+
mk('alpha', 'working'),
|
|
49
|
+
mk('beta', 'working'),
|
|
50
|
+
mk('gamma', 'idle'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
56
|
+
id, from_alias, to_alias, content: 'x', network_id: 'default',
|
|
57
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
// 12 alpha→beta = HOT, 4 beta→gamma = warm
|
|
60
|
+
const msgs = [];
|
|
61
|
+
for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, 'alpha', 'beta', 20000 + i * 500));
|
|
62
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, 'beta', 'gamma', 30000 + i * 500));
|
|
63
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
64
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
65
|
+
|
|
66
|
+
const page = await ctx.newPage();
|
|
67
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
68
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
69
|
+
await page.waitForSelector('[data-edge-count-badge="alpha->beta"]', { timeout: 10000 });
|
|
70
|
+
await page.waitForTimeout(400);
|
|
71
|
+
|
|
72
|
+
const inspect = (key) => page.evaluate((k) => {
|
|
73
|
+
const g = document.querySelector(`[data-edge-count-badge="${k}"]`);
|
|
74
|
+
if (!g) return null;
|
|
75
|
+
const circle = g.querySelector('circle');
|
|
76
|
+
return {
|
|
77
|
+
hot: g.getAttribute('data-edge-count-badge-hot'),
|
|
78
|
+
pinned: g.getAttribute('data-edge-count-badge-pinned'),
|
|
79
|
+
stroke: circle?.getAttribute('stroke'),
|
|
80
|
+
strokeWidth: circle?.getAttribute('stroke-width'),
|
|
81
|
+
};
|
|
82
|
+
}, key);
|
|
83
|
+
|
|
84
|
+
// State 1 + 2: before any pin
|
|
85
|
+
const a2b = await inspect('alpha->beta');
|
|
86
|
+
const b2g = await inspect('beta->gamma');
|
|
87
|
+
|
|
88
|
+
// State 3: pin alpha→beta — pin wins over hot
|
|
89
|
+
await page.locator('[data-edge-count-badge="alpha->beta"]').click();
|
|
90
|
+
await page.waitForTimeout(200);
|
|
91
|
+
const a2bPinned = await inspect('alpha->beta');
|
|
92
|
+
|
|
93
|
+
await browser.close();
|
|
94
|
+
|
|
95
|
+
// Cyber theme amber: #fbbf24
|
|
96
|
+
const amberHex = '#fbbf24';
|
|
97
|
+
const flowEdgeNotAmber = (s) => !!s && s.toLowerCase() !== amberHex.toLowerCase();
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
hot_present: a2b !== null && a2b.hot === 'true',
|
|
101
|
+
hot_strokeAmber: a2b !== null && (a2b.stroke || '').toLowerCase() === amberHex.toLowerCase(),
|
|
102
|
+
hot_strokeWidth2: a2b !== null && a2b.strokeWidth === '2',
|
|
103
|
+
warm_notHot: b2g !== null && b2g.hot === 'false',
|
|
104
|
+
warm_strokeNotAmber: b2g !== null && flowEdgeNotAmber(b2g.stroke),
|
|
105
|
+
warm_strokeWidth1: b2g !== null && b2g.strokeWidth === '1',
|
|
106
|
+
pin_winsOverHot: a2bPinned !== null && a2bPinned.pinned === 'true' && a2bPinned.hot === 'true'
|
|
107
|
+
&& (a2bPinned.stroke || '').toLowerCase() !== amberHex.toLowerCase()
|
|
108
|
+
&& a2bPinned.strokeWidth === '2',
|
|
109
|
+
};
|
|
110
|
+
const ok = Object.values(results).every(Boolean);
|
|
111
|
+
console.log(`${ok ? '✅' : '❌'} edge-badge hot:`, JSON.stringify(results),
|
|
112
|
+
`\n a2b(12msg)=`, a2b,
|
|
113
|
+
`\n b2g(4msg)=`, b2g,
|
|
114
|
+
`\n a2b-pinned=`, a2bPinned);
|
|
115
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 122 verification: hovering the edge midpoint count badge
|
|
2
|
+
* (R100/R121) sets hoveredEdgeKey — same as hovering the line
|
|
3
|
+
* itself (R48). Endpoint rings light, edge brightens. R121 only
|
|
4
|
+
* wired click; R122 closes the hover consistency gap.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: alpha→beta with 4 msgs (badge renders), gamma idle.
|
|
7
|
+
* Hover the badge → alpha + beta get endpoint rings (R111),
|
|
8
|
+
* gamma stays bare. mouseleave → rings clear.
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const browser = await chromium.launch({ headless: true });
|
|
15
|
+
|
|
16
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
17
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
18
|
+
await ctx.addInitScript(() => {
|
|
19
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
23
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
24
|
+
const r = await route.fetch();
|
|
25
|
+
const b = await r.json();
|
|
26
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
27
|
+
const mk = (alias) => ({
|
|
28
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
29
|
+
network_id: nid, project_dir: null,
|
|
30
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
31
|
+
});
|
|
32
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
37
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
38
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
41
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
42
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
43
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
44
|
+
mkMsg('m4', 'alpha', 'beta', 60 * 1000),
|
|
45
|
+
] } }));
|
|
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 === 3, { timeout: 30000 });
|
|
50
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
const rings = () => page.evaluate(() => ({
|
|
54
|
+
alpha: !!document.querySelector('g[data-node="alpha"] [data-edge-endpoint-ring]'),
|
|
55
|
+
beta: !!document.querySelector('g[data-node="beta"] [data-edge-endpoint-ring]'),
|
|
56
|
+
gamma: !!document.querySelector('g[data-node="gamma"] [data-edge-endpoint-ring]'),
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
const before = await rings();
|
|
60
|
+
|
|
61
|
+
// Hover the badge.
|
|
62
|
+
await page.locator('[data-edge-count-badge="alpha->beta"]').hover();
|
|
63
|
+
await page.waitForTimeout(300);
|
|
64
|
+
const onHover = await rings();
|
|
65
|
+
|
|
66
|
+
// Mouse far away.
|
|
67
|
+
await page.mouse.move(10, 10);
|
|
68
|
+
await page.waitForTimeout(300);
|
|
69
|
+
const afterLeave = await rings();
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const results = {
|
|
74
|
+
before_noRings: !before.alpha && !before.beta && !before.gamma,
|
|
75
|
+
hover_alphaRing: onHover.alpha === true,
|
|
76
|
+
hover_betaRing: onHover.beta === true,
|
|
77
|
+
hover_gammaBare: onHover.gamma === false,
|
|
78
|
+
leave_allClear: !afterLeave.alpha && !afterLeave.beta && !afterLeave.gamma,
|
|
79
|
+
};
|
|
80
|
+
const ok = Object.values(results).every(Boolean);
|
|
81
|
+
console.log(`${ok ? '✅' : '❌'} edge-badge hover:`, JSON.stringify(results),
|
|
82
|
+
`\n before=`, before,
|
|
83
|
+
`\n onHover=`, onHover,
|
|
84
|
+
`\n afterLeave=`, afterLeave);
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* Round 164 verification: edge midpoint count badge gains
|
|
2
|
+
* hover-lift (6th surface in the hover-elevation idiom family).
|
|
3
|
+
*
|
|
4
|
+
* Pre-R164 the badge had R122 hover→edge-brighten propagation
|
|
5
|
+
* but the badge itself stayed static (r=9, strokeWidth=1 or 2).
|
|
6
|
+
* The hover-elevation family covered nodes/panels/group boxes/
|
|
7
|
+
* recent rows/legend rows but not the canvas edge badges.
|
|
8
|
+
*
|
|
9
|
+
* R164:
|
|
10
|
+
* r = (isHoveredEdge || isPinned) ? 10.5 : 9
|
|
11
|
+
* transition r 180ms ease-out
|
|
12
|
+
* data-edge-badge-lifted = 'true' | 'false'
|
|
13
|
+
*
|
|
14
|
+
* Pin still keeps its R121 stroke change (legendHeadline +
|
|
15
|
+
* width 2). Hover and pin share the radius lift.
|
|
16
|
+
*
|
|
17
|
+
* Test:
|
|
18
|
+
* 1. Mock 4 msgs on alpha→beta (R100 threshold = count >= 3)
|
|
19
|
+
* 2. Probe idle radius = 9
|
|
20
|
+
* 3. Hover the edge line → badge r = 10.5
|
|
21
|
+
* 4. Mouseleave → r = 9 again
|
|
22
|
+
* 5. Click badge → pin, r = 10.5 + stroke change
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias) => ({
|
|
42
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
// 4 sessions so alpha and beta sit 90° apart on the ring
|
|
47
|
+
// (not 180° apart — which would put alpha→beta midpoint
|
|
48
|
+
// exactly on the hub at 500,330 and let the hub's r=18
|
|
49
|
+
// halo intercept the badge hover). gamma + delta are
|
|
50
|
+
// filler with no flows; the only edge badge that renders
|
|
51
|
+
// is alpha↔beta.
|
|
52
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
53
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
54
|
+
] } });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const msgs = [];
|
|
59
|
+
for (let i = 0; i < 4; i++) {
|
|
60
|
+
msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
61
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
|
|
62
|
+
}
|
|
63
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
64
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
65
|
+
|
|
66
|
+
const page = await ctx.newPage();
|
|
67
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
68
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
69
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
70
|
+
await page.waitForTimeout(400);
|
|
71
|
+
|
|
72
|
+
// Probe idle state
|
|
73
|
+
const idle = await page.evaluate(() => {
|
|
74
|
+
const g = document.querySelector('[data-edge-count-badge]');
|
|
75
|
+
if (!g) return null;
|
|
76
|
+
const c = g.querySelector('circle');
|
|
77
|
+
return {
|
|
78
|
+
r: parseFloat(c.getAttribute('r')),
|
|
79
|
+
lifted: c.getAttribute('data-edge-badge-lifted'),
|
|
80
|
+
stroke: c.getAttribute('stroke'),
|
|
81
|
+
strokeWidth: parseFloat(c.getAttribute('stroke-width')),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Hover the badge (it also propagates to hoveredEdgeKey)
|
|
86
|
+
await page.locator('[data-edge-count-badge]').hover();
|
|
87
|
+
await page.waitForTimeout(300);
|
|
88
|
+
const hovered = await page.evaluate(() => {
|
|
89
|
+
const g = document.querySelector('[data-edge-count-badge]');
|
|
90
|
+
if (!g) return null;
|
|
91
|
+
const c = g.querySelector('circle');
|
|
92
|
+
return {
|
|
93
|
+
r: parseFloat(c.getAttribute('r')),
|
|
94
|
+
lifted: c.getAttribute('data-edge-badge-lifted'),
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Move cursor away to a known empty area
|
|
99
|
+
await page.mouse.move(10, 10);
|
|
100
|
+
await page.waitForTimeout(300);
|
|
101
|
+
const idleAgain = await page.evaluate(() => {
|
|
102
|
+
const g = document.querySelector('[data-edge-count-badge]');
|
|
103
|
+
if (!g) return null;
|
|
104
|
+
const c = g.querySelector('circle');
|
|
105
|
+
return {
|
|
106
|
+
r: parseFloat(c.getAttribute('r')),
|
|
107
|
+
lifted: c.getAttribute('data-edge-badge-lifted'),
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Click badge → pin
|
|
112
|
+
await page.locator('[data-edge-count-badge]').click({ force: true });
|
|
113
|
+
await page.waitForTimeout(300);
|
|
114
|
+
// Move away to clear hover, then probe pinned state.
|
|
115
|
+
await page.mouse.move(10, 10);
|
|
116
|
+
await page.waitForTimeout(300);
|
|
117
|
+
const pinned = await page.evaluate(() => {
|
|
118
|
+
const g = document.querySelector('[data-edge-count-badge]');
|
|
119
|
+
if (!g) return null;
|
|
120
|
+
const c = g.querySelector('circle');
|
|
121
|
+
return {
|
|
122
|
+
r: parseFloat(c.getAttribute('r')),
|
|
123
|
+
lifted: c.getAttribute('data-edge-badge-lifted'),
|
|
124
|
+
strokeWidth: parseFloat(c.getAttribute('stroke-width')),
|
|
125
|
+
pinnedAttr: g.getAttribute('data-edge-count-badge-pinned'),
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await browser.close();
|
|
130
|
+
|
|
131
|
+
const results = {
|
|
132
|
+
idle_r_9: idle && Math.abs(idle.r - 9) < 0.01,
|
|
133
|
+
idle_lifted_false: idle && idle.lifted === 'false',
|
|
134
|
+
idle_strokeWidth_1: idle && idle.strokeWidth === 1,
|
|
135
|
+
hover_r_10p5: hovered && Math.abs(hovered.r - 10.5) < 0.01,
|
|
136
|
+
hover_lifted_true: hovered && hovered.lifted === 'true',
|
|
137
|
+
idle_again_r_9: idleAgain && Math.abs(idleAgain.r - 9) < 0.01,
|
|
138
|
+
idle_again_lifted_false: idleAgain && idleAgain.lifted === 'false',
|
|
139
|
+
pinned_r_10p5: pinned && Math.abs(pinned.r - 10.5) < 0.01,
|
|
140
|
+
pinned_lifted_true: pinned && pinned.lifted === 'true',
|
|
141
|
+
pinned_strokeWidth_2: pinned && pinned.strokeWidth === 2,
|
|
142
|
+
pinned_attr_true: pinned && pinned.pinnedAttr === 'true',
|
|
143
|
+
};
|
|
144
|
+
const ok = Object.values(results).every(Boolean);
|
|
145
|
+
console.log(`${ok ? '✅' : '❌'} edge badge lift:`, JSON.stringify(results),
|
|
146
|
+
`\n idle =`, idle,
|
|
147
|
+
`\n hovered =`, hovered,
|
|
148
|
+
`\n idleAgain =`, idleAgain,
|
|
149
|
+
`\n pinned =`, pinned);
|
|
150
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Round 185 verification: edge badge click fires a one-shot
|
|
2
|
+
* ripple at the badge midpoint — closes the click-feel idiom
|
|
3
|
+
* across hub (R52) / node (R14) / edge badge (R185).
|
|
4
|
+
*
|
|
5
|
+
* Test:
|
|
6
|
+
* 1. Mock 4 sessions + 4-msg alpha→beta flow (R100 threshold)
|
|
7
|
+
* 2. Idle: no ripple <circle> visible
|
|
8
|
+
* 3. Click edge midpoint badge → ripple circle appears with
|
|
9
|
+
* stroke=pal.flowEdge (#67e8f9 cyber), positioned at
|
|
10
|
+
* badge coords (close to alpha-beta midpoint with R164
|
|
11
|
+
* perpendicular lift offset)
|
|
12
|
+
* 4. Wait 700ms → ripple cleared (R14 600ms + buffer)
|
|
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: 1500, 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
|
+
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
28
|
+
const r = await route.fetch();
|
|
29
|
+
const b = await r.json();
|
|
30
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
31
|
+
const mk = (alias) => ({
|
|
32
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
36
|
+
// 4 sessions so alpha/beta sit 90° apart (avoid hub overlap)
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
38
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
39
|
+
] } });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const msgs = [];
|
|
44
|
+
for (let i = 0; i < 4; i++) {
|
|
45
|
+
msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
46
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
|
|
47
|
+
}
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
54
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
55
|
+
await page.waitForTimeout(400);
|
|
56
|
+
|
|
57
|
+
// Idle: no click ripple anywhere. The ripple is identified by:
|
|
58
|
+
// <circle stroke="#67e8f9" stroke-width="2" opacity="0"> with an animate child
|
|
59
|
+
// Probe via "circles with stroke-width=2 and a child <animate>".
|
|
60
|
+
const probeRipple = () => page.evaluate(() => {
|
|
61
|
+
const circles = [...document.querySelectorAll('circle[stroke-width="2"]')];
|
|
62
|
+
const ripple = circles.find(c => {
|
|
63
|
+
const anim = c.querySelector('animate[attributeName="r"]');
|
|
64
|
+
return anim && anim.getAttribute('dur') === '0.5s';
|
|
65
|
+
});
|
|
66
|
+
if (!ripple) return null;
|
|
67
|
+
return {
|
|
68
|
+
cx: parseFloat(ripple.getAttribute('cx') || ''),
|
|
69
|
+
cy: parseFloat(ripple.getAttribute('cy') || ''),
|
|
70
|
+
stroke: ripple.getAttribute('stroke'),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const idle = await probeRipple();
|
|
75
|
+
|
|
76
|
+
// Capture badge position first
|
|
77
|
+
const badgeBox = await page.locator('[data-edge-count-badge]').first().boundingBox();
|
|
78
|
+
|
|
79
|
+
// Click the edge midpoint badge → ripple should appear
|
|
80
|
+
await page.locator('[data-edge-count-badge]').click();
|
|
81
|
+
await page.waitForTimeout(50);
|
|
82
|
+
const duringRipple = await probeRipple();
|
|
83
|
+
|
|
84
|
+
// Wait past 600ms ripple lifetime + buffer
|
|
85
|
+
await page.waitForTimeout(700);
|
|
86
|
+
const afterRipple = await probeRipple();
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
idle_no_ripple: idle === null,
|
|
92
|
+
ripple_appears: duringRipple !== null,
|
|
93
|
+
ripple_stroke_cyan: duringRipple?.stroke === '#67e8f9',
|
|
94
|
+
ripple_has_position: duringRipple !== null
|
|
95
|
+
&& Number.isFinite(duringRipple.cx)
|
|
96
|
+
&& Number.isFinite(duringRipple.cy),
|
|
97
|
+
// The badge has bbox in screen coords; the ripple is in SVG viewBox
|
|
98
|
+
// coords. We can't compare directly. Just verify it's a sane number
|
|
99
|
+
// (not 0/NaN) — that's enough to know it's placed at the badge.
|
|
100
|
+
ripple_cx_nonzero: duringRipple !== null && duringRipple.cx > 0,
|
|
101
|
+
ripple_cy_nonzero: duringRipple !== null && duringRipple.cy > 0,
|
|
102
|
+
ripple_cleared_after_600: afterRipple === null,
|
|
103
|
+
badge_was_clickable: badgeBox !== null,
|
|
104
|
+
};
|
|
105
|
+
const ok = Object.values(results).every(Boolean);
|
|
106
|
+
console.log(`${ok ? '✅' : '❌'} edge badge ripple:`, JSON.stringify(results),
|
|
107
|
+
`\n idle:`, idle,
|
|
108
|
+
`\n duringRipple:`, duringRipple,
|
|
109
|
+
`\n afterRipple:`, afterRipple,
|
|
110
|
+
`\n badgeBox:`, badgeBox);
|
|
111
|
+
process.exit(ok ? 0 : 1);
|