@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,137 @@
|
|
|
1
|
+
/* Round 220 verification (milestone): recent-signal row text completes
|
|
2
|
+
* the pin-signature typography triple (R218 group labels / R219
|
|
3
|
+
* legend rows / R220 recent-signal rows). All three label-based
|
|
4
|
+
* interactive surfaces now read "locked in" at type level on pin.
|
|
5
|
+
*
|
|
6
|
+
* Test (cyber dark, 4 working sessions + 3 mock flows so recent-
|
|
7
|
+
* signal panel renders 3 rows):
|
|
8
|
+
* 1. 3 [data-recent-row-text] rows render.
|
|
9
|
+
* 2. Idle: all pinned='false', spacing 'normal' or 0px.
|
|
10
|
+
* 3. Inline transition includes 'letter-spacing 150ms'.
|
|
11
|
+
* 4. Click first row to pin it.
|
|
12
|
+
* 5. First row: pinned='true', spacing ≈ 0.5px.
|
|
13
|
+
* 6. Other rows stay unpinned + 0 spacing.
|
|
14
|
+
* 7. Click pinned row again → unpin → back to 0.
|
|
15
|
+
* 8. Hover (not pin) does NOT trigger letter-spacing spread —
|
|
16
|
+
* pin-exclusive signature (proven by hover-without-click test).
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const browser = await chromium.launch({ headless: true });
|
|
23
|
+
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const msgs = [
|
|
47
|
+
{ id: '1', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
48
|
+
network_id: 'default', created_at: new Date(now - 2000).toISOString() },
|
|
49
|
+
{ id: '2', from_alias: 'beta', to_alias: 'gamma', content: 'hi',
|
|
50
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
51
|
+
{ id: '3', from_alias: 'gamma', to_alias: 'delta', content: 'hi',
|
|
52
|
+
network_id: 'default', created_at: new Date(now - 8000).toISOString() },
|
|
53
|
+
];
|
|
54
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: msgs } }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
56
|
+
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
60
|
+
await page.waitForSelector('[data-recent-row-text]', { timeout: 10000, state: 'attached' });
|
|
61
|
+
await page.waitForTimeout(300);
|
|
62
|
+
|
|
63
|
+
const probe = async () => page.evaluate(() => {
|
|
64
|
+
return [...document.querySelectorAll('[data-recent-row-text]')].map(el => {
|
|
65
|
+
const cs = getComputedStyle(el);
|
|
66
|
+
return {
|
|
67
|
+
key: el.getAttribute('data-recent-row-text'),
|
|
68
|
+
pinned: el.getAttribute('data-recent-row-text-pinned'),
|
|
69
|
+
spacing: cs.letterSpacing,
|
|
70
|
+
transition: el.style.transition,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const idle = await probe();
|
|
76
|
+
const firstKey = idle[0]?.key;
|
|
77
|
+
|
|
78
|
+
// Hover-without-pin probe: hover the first row, verify spacing
|
|
79
|
+
// stays at 0 (pin-exclusive signature, not triggered by hover)
|
|
80
|
+
await page.hover(`[data-recent-row="${firstKey}"]`);
|
|
81
|
+
await page.waitForTimeout(200);
|
|
82
|
+
const hovered = await probe();
|
|
83
|
+
await page.mouse.move(0, 0);
|
|
84
|
+
await page.waitForTimeout(180);
|
|
85
|
+
|
|
86
|
+
// Click to pin
|
|
87
|
+
await page.click(`[data-recent-row="${firstKey}"]`);
|
|
88
|
+
await page.waitForTimeout(200);
|
|
89
|
+
const pinned = await probe();
|
|
90
|
+
|
|
91
|
+
// Click again to unpin
|
|
92
|
+
await page.click(`[data-recent-row="${firstKey}"]`);
|
|
93
|
+
await page.waitForTimeout(200);
|
|
94
|
+
const unpinned = await probe();
|
|
95
|
+
|
|
96
|
+
await browser.close();
|
|
97
|
+
|
|
98
|
+
const near = (s, want, tol = 0.15) => {
|
|
99
|
+
if (want === 0 && (s === 'normal' || s === '0px' || s === '0' || !s)) return true;
|
|
100
|
+
const got = parseFloat(s || '0');
|
|
101
|
+
return Math.abs(got - want) < tol;
|
|
102
|
+
};
|
|
103
|
+
const hasLetterSpacingT = (s) => /letter-spacing\s+(?:150ms|0\.15s)/.test(s || '');
|
|
104
|
+
|
|
105
|
+
const firstIdle = idle.find(p => p.key === firstKey);
|
|
106
|
+
const firstHovered = hovered.find(p => p.key === firstKey);
|
|
107
|
+
const firstPinned = pinned.find(p => p.key === firstKey);
|
|
108
|
+
const otherPinned = pinned.find(p => p.key !== firstKey);
|
|
109
|
+
const firstUnpin = unpinned.find(p => p.key === firstKey);
|
|
110
|
+
|
|
111
|
+
const results = {
|
|
112
|
+
three_rows_present: idle.length === 3,
|
|
113
|
+
all_idle_unpinned: idle.every(p => p.pinned === 'false'),
|
|
114
|
+
all_idle_spacing_zero: idle.every(p => near(p.spacing, 0)),
|
|
115
|
+
all_have_transition: idle.every(p => hasLetterSpacingT(p.transition)),
|
|
116
|
+
|
|
117
|
+
// Pin-exclusive: hover does NOT trigger letter-spacing spread
|
|
118
|
+
hovered_pinned_false: firstHovered?.pinned === 'false',
|
|
119
|
+
hovered_spacing_zero: near(firstHovered?.spacing, 0),
|
|
120
|
+
|
|
121
|
+
// Pin click triggers spread
|
|
122
|
+
first_pinned_attr: firstPinned?.pinned === 'true',
|
|
123
|
+
first_pinned_spacing: near(firstPinned?.spacing, 0.5),
|
|
124
|
+
other_stays_unpinned: otherPinned?.pinned === 'false',
|
|
125
|
+
other_spacing_unchanged: near(otherPinned?.spacing, 0),
|
|
126
|
+
|
|
127
|
+
// Unpin returns to 0
|
|
128
|
+
unpin_attr_false: firstUnpin?.pinned === 'false',
|
|
129
|
+
unpin_spacing_zero: near(firstUnpin?.spacing, 0),
|
|
130
|
+
};
|
|
131
|
+
const ok = Object.values(results).every(Boolean);
|
|
132
|
+
console.log(`${ok ? '✅' : '❌'} recent-row pin spacing:`, JSON.stringify(results),
|
|
133
|
+
`\n idle:`, idle,
|
|
134
|
+
`\n hovered:`, hovered,
|
|
135
|
+
`\n pinned:`, pinned,
|
|
136
|
+
`\n unpinned:`,unpinned);
|
|
137
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* Round 116 verification: recent-signal row click pins the edge.
|
|
2
|
+
* R56 made hover transient; R116 makes click sticky — survives
|
|
3
|
+
* mouseleave, releases on second click or Esc. activeEdgeKey =
|
|
4
|
+
* hoveredEdgeKey ?? pinnedEdgeKey, so hover-while-pinned previews
|
|
5
|
+
* another edge transiently and mouseleave falls back to the pin.
|
|
6
|
+
*
|
|
7
|
+
* Drives 2 flow rows. Click row 0 → row's aria-pressed=true,
|
|
8
|
+
* data-recent-row-pinned=true, the edge stays bright after mouseleave
|
|
9
|
+
* (non-endpoints stay dim, endpoint ring stays). Click row 0 again
|
|
10
|
+
* → release. Pin row 0, hover row 1 → row-1 preview wins; leave →
|
|
11
|
+
* row-0 pin restores. Esc clears.
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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
|
+
|
|
25
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
26
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
27
|
+
const r = await route.fetch();
|
|
28
|
+
const b = await r.json();
|
|
29
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
30
|
+
const mk = (alias) => ({
|
|
31
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
32
|
+
network_id: nid, project_dir: null,
|
|
33
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
34
|
+
});
|
|
35
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
40
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
41
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
44
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
45
|
+
mkMsg('m2', 'gamma', 'delta', 60 * 1000),
|
|
46
|
+
] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
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-recent-row]', { timeout: 10000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const rowState = (key) => page.evaluate((k) => {
|
|
55
|
+
const g = document.querySelector(`[data-recent-row="${k}"]`);
|
|
56
|
+
return {
|
|
57
|
+
pinned: g?.getAttribute('data-recent-row-pinned') || '',
|
|
58
|
+
aria: g?.getAttribute('aria-pressed') || '',
|
|
59
|
+
};
|
|
60
|
+
}, key);
|
|
61
|
+
const endpointRingFor = (alias) => page.evaluate((a) => {
|
|
62
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
63
|
+
return !!g?.querySelector('[data-edge-endpoint-ring]');
|
|
64
|
+
}, alias);
|
|
65
|
+
|
|
66
|
+
const before = await rowState('alpha->beta');
|
|
67
|
+
|
|
68
|
+
// Click row 0 → pin.
|
|
69
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
70
|
+
await page.mouse.move(10, 10);
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
const afterPin = await rowState('alpha->beta');
|
|
73
|
+
const alphaRingPin = await endpointRingFor('alpha');
|
|
74
|
+
const betaRingPin = await endpointRingFor('beta');
|
|
75
|
+
const gammaRingPin = await endpointRingFor('gamma');
|
|
76
|
+
|
|
77
|
+
// Click again → release.
|
|
78
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
79
|
+
await page.mouse.move(10, 10);
|
|
80
|
+
await page.waitForTimeout(300);
|
|
81
|
+
const afterRelease = await rowState('alpha->beta');
|
|
82
|
+
const alphaRingRelease = await endpointRingFor('alpha');
|
|
83
|
+
|
|
84
|
+
// Pin row 0 again, hover row 1 — preview wins.
|
|
85
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
86
|
+
await page.mouse.move(10, 10);
|
|
87
|
+
await page.waitForTimeout(200);
|
|
88
|
+
await page.locator('[data-recent-row="gamma->delta"]').hover();
|
|
89
|
+
await page.waitForTimeout(300);
|
|
90
|
+
const alphaWhileHoverGamma = await endpointRingFor('alpha');
|
|
91
|
+
const gammaWhileHover = await endpointRingFor('gamma');
|
|
92
|
+
|
|
93
|
+
// Leave gamma row — row-0 pin restores.
|
|
94
|
+
await page.mouse.move(10, 10);
|
|
95
|
+
await page.waitForTimeout(300);
|
|
96
|
+
const alphaAfterLeave = await endpointRingFor('alpha');
|
|
97
|
+
const gammaAfterLeave = await endpointRingFor('gamma');
|
|
98
|
+
|
|
99
|
+
// Esc clears.
|
|
100
|
+
await page.keyboard.press('Escape');
|
|
101
|
+
await page.waitForTimeout(300);
|
|
102
|
+
const afterEsc = await rowState('alpha->beta');
|
|
103
|
+
const alphaAfterEsc = await endpointRingFor('alpha');
|
|
104
|
+
|
|
105
|
+
await browser.close();
|
|
106
|
+
|
|
107
|
+
const results = {
|
|
108
|
+
before_notPinned: before.pinned === 'false' && before.aria === 'false',
|
|
109
|
+
pin_attrTrue: afterPin.pinned === 'true' && afterPin.aria === 'true',
|
|
110
|
+
pin_endpointsLit: alphaRingPin === true && betaRingPin === true,
|
|
111
|
+
pin_nonEndpointDark: gammaRingPin === false,
|
|
112
|
+
releaseAfterReclick: afterRelease.pinned === 'false' && alphaRingRelease === false,
|
|
113
|
+
hoverPreview_gammaLit: gammaWhileHover === true,
|
|
114
|
+
hoverPreview_alphaDark: alphaWhileHoverGamma === false,
|
|
115
|
+
leave_pinRestored: alphaAfterLeave === true && gammaAfterLeave === false,
|
|
116
|
+
esc_clearsPin: afterEsc.pinned === 'false' && alphaAfterEsc === false,
|
|
117
|
+
};
|
|
118
|
+
const ok = Object.values(results).every(Boolean);
|
|
119
|
+
console.log(`${ok ? '✅' : '❌'} recent-row pin:`, JSON.stringify(results),
|
|
120
|
+
`\n afterPin=`, afterPin,
|
|
121
|
+
`\n rings@pin: alpha=`, alphaRingPin, ' beta=', betaRingPin, ' gamma=', gammaRingPin,
|
|
122
|
+
`\n afterRelease=`, afterRelease,
|
|
123
|
+
`\n hoverPreview: alpha=`, alphaWhileHoverGamma, ' gamma=', gammaWhileHover,
|
|
124
|
+
`\n afterLeave: alpha=`, alphaAfterLeave, ' gamma=', gammaAfterLeave,
|
|
125
|
+
`\n afterEsc=`, afterEsc);
|
|
126
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* Round 104 verification: recent-signal panel rows get a subtle
|
|
2
|
+
* row-background tint on hover. R56 already brightens the matching
|
|
3
|
+
* edge on the canvas, but the panel row itself stayed flat — the
|
|
4
|
+
* row now has visible feedback at the source surface.
|
|
5
|
+
*
|
|
6
|
+
* Driver: 3 flow rows. Initial state — all hitbox rects are
|
|
7
|
+
* transparent. Hover row 0 — its rect fills with the accent colour
|
|
8
|
+
* at low alpha + data-recent-row-hovered="true"; the other two rows
|
|
9
|
+
* stay transparent + "false". Mouseleave — back to all-false.
|
|
10
|
+
*/
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
15
|
+
const browser = await chromium.launch({ headless: true });
|
|
16
|
+
|
|
17
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const mk = (alias) => ({
|
|
29
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
30
|
+
network_id: nid, project_dir: null,
|
|
31
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
32
|
+
});
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
38
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
39
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
40
|
+
});
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
42
|
+
mkMsg('m1', 'alpha', 'beta', 20 * 1000),
|
|
43
|
+
mkMsg('m2', 'gamma', 'delta', 30 * 1000),
|
|
44
|
+
mkMsg('m3', 'alpha', 'gamma', 40 * 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 === 4, { timeout: 30000 });
|
|
50
|
+
await page.waitForSelector('[data-recent-row]', { timeout: 10000 });
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
const readRows = () => page.evaluate(() => {
|
|
54
|
+
return [...document.querySelectorAll('[data-recent-row]')].map(g => {
|
|
55
|
+
const rect = g.querySelector('rect');
|
|
56
|
+
return {
|
|
57
|
+
key: g.getAttribute('data-recent-row'),
|
|
58
|
+
hovered: g.getAttribute('data-recent-row-hovered'),
|
|
59
|
+
rectFill: rect?.getAttribute('fill') || '',
|
|
60
|
+
rectOpac: rect?.getAttribute('opacity') || '',
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const before = await readRows();
|
|
66
|
+
|
|
67
|
+
// Hover the first row.
|
|
68
|
+
await page.locator('[data-recent-row]').first().hover();
|
|
69
|
+
await page.waitForTimeout(250);
|
|
70
|
+
const duringHover = await readRows();
|
|
71
|
+
|
|
72
|
+
// Move pointer away.
|
|
73
|
+
await page.mouse.move(10, 10);
|
|
74
|
+
await page.waitForTimeout(250);
|
|
75
|
+
const afterLeave = await readRows();
|
|
76
|
+
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
before_allTransparent: before.every(r => r.rectFill === 'transparent' && r.hovered === 'false'),
|
|
81
|
+
hover_firstRowTinted: duringHover[0]?.hovered === 'true' && duringHover[0]?.rectFill !== 'transparent',
|
|
82
|
+
hover_otherRowsUnchanged: duringHover.slice(1).every(r => r.hovered === 'false' && r.rectFill === 'transparent'),
|
|
83
|
+
hover_opacityVisibleButLow: duringHover[0] && parseFloat(duringHover[0].rectOpac) > 0 && parseFloat(duringHover[0].rectOpac) < 0.3,
|
|
84
|
+
leave_restored: afterLeave.every(r => r.rectFill === 'transparent' && r.hovered === 'false'),
|
|
85
|
+
};
|
|
86
|
+
const ok = Object.values(results).every(Boolean);
|
|
87
|
+
console.log(`${ok ? '✅' : '❌'} recent-row tint:`, JSON.stringify(results),
|
|
88
|
+
`\n before=`, before,
|
|
89
|
+
`\n duringHover=`, duringHover,
|
|
90
|
+
`\n afterLeave=`, afterLeave);
|
|
91
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Round 148 verification: recent-signal row gains a <title> tooltip
|
|
2
|
+
* exposing full message context — the row text truncates aliases to
|
|
3
|
+
* 6 chars (R127) and content to 8, useful for scan-density but
|
|
4
|
+
* obscures detail. Native SVG <title> reveals the full alias /
|
|
5
|
+
* content / timestamp on hover. Pinned vs unpinned switches the
|
|
6
|
+
* click-hint so the user knows the next gesture's effect.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors R98's enriched node tooltip at the per-row scope.
|
|
9
|
+
*
|
|
10
|
+
* Fleet: alpha sends 12 msgs to beta-with-long-name (HOT), and
|
|
11
|
+
* beta-with-long-name sends 4 msgs to gamma (warm). Content is
|
|
12
|
+
* a long string to verify untruncated display.
|
|
13
|
+
*
|
|
14
|
+
* States tested:
|
|
15
|
+
* - hot row: title contains full from / to / msg count + "hot lane"
|
|
16
|
+
* marker + full content + ISO timestamp + pin hint
|
|
17
|
+
* - warm row: same shape minus the "hot lane" marker
|
|
18
|
+
* - pinned: click-hint flips to "click to release pin"
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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
|
+
|
|
32
|
+
// Aliases longer than the row truncation cap (6) so the title's
|
|
33
|
+
// untruncated form is meaningfully different from what's painted.
|
|
34
|
+
const aliasA = 'alpha-runner';
|
|
35
|
+
const aliasB = 'beta-with-long-name';
|
|
36
|
+
const aliasC = 'gamma';
|
|
37
|
+
const longContent = 'this is the full long message body that the row truncates';
|
|
38
|
+
|
|
39
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
40
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
41
|
+
const r = await route.fetch();
|
|
42
|
+
const b = await r.json();
|
|
43
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
44
|
+
const mk = (alias, status) => ({
|
|
45
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
50
|
+
mk(aliasA, 'working'), mk(aliasB, 'working'), mk(aliasC, 'idle'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const mkMsg = (id, from, to, ageMs) => ({
|
|
56
|
+
id, from_alias: from, to_alias: to, content: longContent,
|
|
57
|
+
network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
const msgs = [];
|
|
60
|
+
// 12 alpha→beta = HOT
|
|
61
|
+
for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, aliasA, aliasB, 20000 + i * 500));
|
|
62
|
+
// 4 beta→gamma = warm
|
|
63
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, aliasB, aliasC, 30000 + i * 500));
|
|
64
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
65
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
66
|
+
|
|
67
|
+
const page = await ctx.newPage();
|
|
68
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
69
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
70
|
+
await page.waitForSelector(`[data-recent-row="${aliasA}->${aliasB}"]`, { timeout: 10000 });
|
|
71
|
+
await page.waitForTimeout(400);
|
|
72
|
+
|
|
73
|
+
const readTitle = (key) => page.evaluate((k) => {
|
|
74
|
+
const row = document.querySelector(`[data-recent-row="${k}"]`);
|
|
75
|
+
return row?.querySelector('title')?.textContent || '';
|
|
76
|
+
}, key);
|
|
77
|
+
|
|
78
|
+
const hotTitle = await readTitle(`${aliasA}->${aliasB}`);
|
|
79
|
+
const warmTitle = await readTitle(`${aliasB}->${aliasC}`);
|
|
80
|
+
|
|
81
|
+
// Pin the hot row → title's click-hint flips
|
|
82
|
+
await page.locator(`[data-recent-row="${aliasA}->${aliasB}"]`).click();
|
|
83
|
+
await page.waitForTimeout(200);
|
|
84
|
+
const pinnedTitle = await readTitle(`${aliasA}->${aliasB}`);
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
|
|
88
|
+
const results = {
|
|
89
|
+
// Hot row
|
|
90
|
+
hot_hasFullFrom: hotTitle.includes(aliasA),
|
|
91
|
+
hot_hasFullTo: hotTitle.includes(aliasB),
|
|
92
|
+
hot_hasCount12: hotTitle.includes('12 msgs'),
|
|
93
|
+
hot_hasHotMarker: hotTitle.includes('hot lane'),
|
|
94
|
+
hot_hasFullContent: hotTitle.includes(longContent),
|
|
95
|
+
hot_hasTimestamp: /last: \d/.test(hotTitle),
|
|
96
|
+
hot_pinHint: hotTitle.includes('click to pin'),
|
|
97
|
+
|
|
98
|
+
// Warm row
|
|
99
|
+
warm_hasCount4: warmTitle.includes('4 msgs'),
|
|
100
|
+
warm_noHotMarker: !warmTitle.includes('hot lane'),
|
|
101
|
+
warm_pinHint: warmTitle.includes('click to pin'),
|
|
102
|
+
|
|
103
|
+
// Pinned row's title flips hint
|
|
104
|
+
pinned_releaseHint: pinnedTitle.includes('click to release pin'),
|
|
105
|
+
pinned_noPinHint: !pinnedTitle.includes('click to pin · hover to preview'),
|
|
106
|
+
};
|
|
107
|
+
const ok = Object.values(results).every(Boolean);
|
|
108
|
+
console.log(`${ok ? '✅' : '❌'} recent-row title:`, JSON.stringify(results),
|
|
109
|
+
`\n hotTitle:\n${hotTitle}`,
|
|
110
|
+
`\n warmTitle:\n${warmTitle}`,
|
|
111
|
+
`\n pinnedTitle:\n${pinnedTitle}`);
|
|
112
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Round 191 verification: recent-signal row timestamp text
|
|
2
|
+
* (right edge) picks up the freshness ramp.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R191 the timestamp was static opacity=0.55 regardless of
|
|
5
|
+
* recency. R191 mirrors R160's left-edge pip with its own
|
|
6
|
+
* alpha range:
|
|
7
|
+
* ≤30s → 0.85
|
|
8
|
+
* 30-300s → 0.85 → 0.30 smooth decay
|
|
9
|
+
* >300s → 0.30 (stale floor)
|
|
10
|
+
*
|
|
11
|
+
* Test:
|
|
12
|
+
* 3 flows at 5s / 90s / 360s ago. Probe their timestamp text
|
|
13
|
+
* on the right of each row.
|
|
14
|
+
* - Fresh: opacity ≈ 0.85
|
|
15
|
+
* - Mid: opacity in (0.55, 0.80)
|
|
16
|
+
* - Stale: opacity ≈ 0.30 (floor)
|
|
17
|
+
* - all have data-recent-row-ts-alpha attr matching
|
|
18
|
+
* - all have transition 'opacity 200ms ease-out'
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
|
|
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
|
+
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias) => ({
|
|
38
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const msgs = [
|
|
49
|
+
{ id: 'f', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
50
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
51
|
+
{ id: 'm', from_alias: 'beta', to_alias: 'gamma', content: 'hi',
|
|
52
|
+
network_id: 'default', created_at: new Date(now - 90000).toISOString() },
|
|
53
|
+
{ id: 's', from_alias: 'gamma', to_alias: 'delta', content: 'hi',
|
|
54
|
+
network_id: 'default', created_at: new Date(now - 360000).toISOString() },
|
|
55
|
+
];
|
|
56
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
57
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
58
|
+
|
|
59
|
+
const page = await ctx.newPage();
|
|
60
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
61
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
62
|
+
await page.waitForSelector('[data-recent-row-ts]', { timeout: 10000 });
|
|
63
|
+
await page.waitForTimeout(400);
|
|
64
|
+
|
|
65
|
+
const probe = await page.evaluate(() => {
|
|
66
|
+
const tss = [...document.querySelectorAll('[data-recent-row-ts]')];
|
|
67
|
+
return tss.map(t => ({
|
|
68
|
+
key: t.getAttribute('data-recent-row-ts'),
|
|
69
|
+
opacity: parseFloat(t.getAttribute('opacity') || ''),
|
|
70
|
+
alphaAttr: parseFloat(t.getAttribute('data-recent-row-ts-alpha') || ''),
|
|
71
|
+
transition: t.style.transition || getComputedStyle(t).transition,
|
|
72
|
+
text: t.textContent,
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await browser.close();
|
|
77
|
+
|
|
78
|
+
// Rows are sorted by recency (newest first)
|
|
79
|
+
const [pFresh, pMid, pStale] = probe;
|
|
80
|
+
const hasOpacityTransition = (s) =>
|
|
81
|
+
(s.transition || '').includes('opacity 200ms') ||
|
|
82
|
+
/opacity\s+0\.2s|opacity\s+200ms/.test(s.transition || '');
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
three_timestamps: probe.length === 3,
|
|
86
|
+
fresh_opacity_0p85: Math.abs(pFresh?.opacity - 0.85) < 0.05,
|
|
87
|
+
mid_opacity_window: pMid && pMid.opacity > 0.55 && pMid.opacity < 0.80,
|
|
88
|
+
stale_opacity_0p30: Math.abs(pStale?.opacity - 0.30) < 0.05,
|
|
89
|
+
fresh_alpha_attr_matches: Math.abs(pFresh?.alphaAttr - pFresh?.opacity) < 0.01,
|
|
90
|
+
mid_alpha_attr_matches: Math.abs(pMid?.alphaAttr - pMid?.opacity) < 0.01,
|
|
91
|
+
stale_alpha_attr_matches: Math.abs(pStale?.alphaAttr - pStale?.opacity) < 0.01,
|
|
92
|
+
ladder_descends: pFresh && pMid && pStale && pFresh.opacity > pMid.opacity && pMid.opacity > pStale.opacity,
|
|
93
|
+
all_have_transition: probe.every(p => hasOpacityTransition(p)),
|
|
94
|
+
fresh_text_5s: /^5s$/.test(pFresh?.text || ''),
|
|
95
|
+
mid_text_1m_or_2m: /^[12]m$/.test(pMid?.text || ''),
|
|
96
|
+
};
|
|
97
|
+
const ok = Object.values(results).every(Boolean);
|
|
98
|
+
console.log(`${ok ? '✅' : '❌'} recent-row ts freshness:`, JSON.stringify(results),
|
|
99
|
+
`\n probes=`, probe);
|
|
100
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 94 verification: each recent-signal panel row now carries a
|
|
2
|
+
* right-aligned relative timestamp (e.g. `2s` / `1m`). Composes with
|
|
3
|
+
* the R56 hover affordance and R57 panel elevation without changing
|
|
4
|
+
* geometry. Mirror what the R42 active-links chip already shows for
|
|
5
|
+
* the most-recent flow, but per-row so users can see which flows
|
|
6
|
+
* are current vs stale.
|
|
7
|
+
*
|
|
8
|
+
* Drive the dashboard with messages at known ages — 30s, 5m, 2h —
|
|
9
|
+
* and assert each row's timestamp matches the expected glyph. Also
|
|
10
|
+
* verify the timestamp <text> is non-interactive (pointerEvents:none)
|
|
11
|
+
* so it doesn't steal hover from the row hitbox.
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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 {
|
|
23
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
24
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
25
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const fresh = new Date(Date.now() - 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) => ({
|
|
35
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Mock messages so flowLinks aggregates them. Three flows at increasing
|
|
43
|
+
// ages — the panel sorts most-recent first (per the recent-signal
|
|
44
|
+
// aggregator), so row 0 is the freshest.
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const mkMsg = (id, from_alias, to_alias, content, ageMs) => ({
|
|
47
|
+
id, from_alias, to_alias, content, network_id: 'default',
|
|
48
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
51
|
+
mkMsg('m1', 'alpha', 'beta', 'fix-the-bug', 30 * 1000), // 30s ago → "30s"
|
|
52
|
+
mkMsg('m2', 'gamma', 'delta', 'review-the-pr', 5 * 60 * 1000), // 5m ago → "5m"
|
|
53
|
+
mkMsg('m3', 'alpha', 'gamma', 'deploy-stage', 2 * 60 * 60 * 1000), // 2h ago → "2h"
|
|
54
|
+
] } }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
56
|
+
const page = await ctx.newPage();
|
|
57
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
58
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
59
|
+
// Wait for at least one recent-signal timestamp to render.
|
|
60
|
+
await page.waitForSelector('[data-recent-row-ts]', { timeout: 10000 });
|
|
61
|
+
await page.waitForTimeout(400);
|
|
62
|
+
|
|
63
|
+
const rows = await page.evaluate(() => {
|
|
64
|
+
return [...document.querySelectorAll('[data-recent-row]')].map(g => {
|
|
65
|
+
const rowKey = g.getAttribute('data-recent-row');
|
|
66
|
+
const tsEl = g.querySelector('[data-recent-row-ts]');
|
|
67
|
+
const rowText = g.querySelector('text:not([data-recent-row-ts])')?.textContent || '';
|
|
68
|
+
const tsText = tsEl?.textContent || null;
|
|
69
|
+
const tsPointerEvents = tsEl ? getComputedStyle(tsEl).pointerEvents : null;
|
|
70
|
+
return { rowKey, rowText, tsText, tsPointerEvents };
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
threeRows: rows.length === 3,
|
|
78
|
+
// Row 0 (freshest) should be alpha→beta around "30s" or "29s" etc.
|
|
79
|
+
row0_hasFreshTs: rows[0] && /^[2-9]\d?s$/.test(rows[0].tsText || ''),
|
|
80
|
+
// Row 1 should read 5m (minutes precision).
|
|
81
|
+
row1_minuteTs: rows[1] && /^[4-6]m$/.test(rows[1].tsText || ''),
|
|
82
|
+
// Row 2 should read ~2h.
|
|
83
|
+
row2_hourTs: rows[2] && /^[12]h$/.test(rows[2].tsText || ''),
|
|
84
|
+
// ts <text> must be non-interactive so it doesn't intercept the row hover.
|
|
85
|
+
ts_nonInteractive: rows.every(r => r.tsPointerEvents === 'none'),
|
|
86
|
+
// Row content shows from/to/count/content via the existing layout.
|
|
87
|
+
// R138: arrow notation unified to unicode `→` (was ASCII `->`),
|
|
88
|
+
// matching filter pills / tooltips / edge-badge titles. Assertion
|
|
89
|
+
// flipped to the new typography contract.
|
|
90
|
+
row0_hasArrow: rows[0]?.rowText.includes('→'),
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} recent-row ts:`, JSON.stringify(results),
|
|
94
|
+
`\n rows=`, rows);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|