@sleep2agi/agent-network-dashboard 0.4.6-preview.33 → 0.4.6-preview.331
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/07lsi~s-c0wvh.css +2 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0mpa-fgyi2k78.js +1 -0
- package/.next/static/chunks/0nlhul7k4hwil.js +1 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/static/chunks/1851sbdemt00t.js +4 -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 +111 -1
- package/app/components/TopoGraph.tsx +6197 -262
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/app/lib/vendorIdentity.ts +14 -3
- package/package.json +2 -2
- package/public/sleep2agi-logo.svg +11 -0
- package/public/vendors/claude.svg +11 -0
- package/public/vendors/minimax.svg +11 -0
- package/public/vendors/openai.svg +12 -0
- package/screenshots/r293-p0/after-full.png +0 -0
- package/screenshots/r293-p0/after-topo.png +0 -0
- package/screenshots/r293-p0/baseline-full.png +0 -0
- package/screenshots/r293-p0/baseline-topo.png +0 -0
- package/screenshots/r293-p0/logo-integrated-full.png +0 -0
- package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
- package/screenshots/r293-p0/v0.10.0-hero3-rfc.md +230 -0
- package/scripts/p0-topo-screenshot.mjs +82 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-active-pulse-ease-test.mjs +120 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-brand-logo-transition-test.mjs +94 -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-chiprow-data-weight-test.mjs +94 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
- package/scripts/topo-chrome-inactive-color-test.mjs +90 -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-chrome-zoom-readout-weight-test.mjs +81 -0
- package/scripts/topo-click-ripple-ease-test.mjs +94 -0
- package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
- package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
- package/scripts/topo-cmdk-pin-test.mjs +123 -0
- package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-corner-radius-unify-test.mjs +103 -0
- package/scripts/topo-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
- package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
- package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
- package/scripts/topo-edge-badge-hot-test.mjs +115 -0
- package/scripts/topo-edge-badge-hover-test.mjs +85 -0
- package/scripts/topo-edge-badge-lift-test.mjs +150 -0
- package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
- package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
- package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
- package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
- package/scripts/topo-edge-count-badge-test.mjs +86 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-fadein-test.mjs +106 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
- package/scripts/topo-empty-state-recenter-test.mjs +107 -0
- package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
- package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
- package/scripts/topo-fullscreen-active-test.mjs +115 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +6 -1
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pin-test.mjs +119 -0
- package/scripts/topo-group-pip-fadein-test.mjs +100 -0
- package/scripts/topo-group-pip-tabular-test.mjs +95 -0
- package/scripts/topo-group-status-pips-test.mjs +103 -0
- package/scripts/topo-groupbox-fadein-test.mjs +95 -0
- package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
- package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
- package/scripts/topo-groupbox-live-test.mjs +100 -0
- package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r118-test.mjs +69 -0
- package/scripts/topo-help-overlay-r123-test.mjs +69 -0
- package/scripts/topo-help-overlay-r134-test.mjs +74 -0
- package/scripts/topo-help-overlay-r137-test.mjs +81 -0
- package/scripts/topo-help-overlay-r141-test.mjs +86 -0
- package/scripts/topo-help-overlay-r153-test.mjs +85 -0
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
- package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
- package/scripts/topo-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-digit-scale-test.mjs +129 -0
- package/scripts/topo-hub-fadein-test.mjs +88 -0
- package/scripts/topo-hub-hover-lift-test.mjs +101 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -0
- package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
- package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
- package/scripts/topo-kicker-color-test.mjs +93 -0
- package/scripts/topo-kicker-tracking-test.mjs +101 -0
- package/scripts/topo-kicker-weight-test.mjs +98 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-legend-compress-test.mjs +107 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -0
- package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
- package/scripts/topo-legend-count-tabular-test.mjs +87 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
- package/scripts/topo-legend-count-weight-test.mjs +86 -0
- package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
- package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
- package/scripts/topo-legend-panel-count-weight-test.mjs +89 -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-labels-simplify-test.mjs +80 -0
- package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-legend-row-title-test.mjs +102 -0
- package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
- package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
- package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-panel-fadein-test.mjs +102 -0
- package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-panel-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
- package/scripts/topo-panel-title-tracking-test.mjs +78 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
- package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
- package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
- package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
- package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
- package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
- package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
- package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-fadein-test.mjs +107 -0
- package/scripts/topo-recent-row-freshness-test.mjs +102 -0
- package/scripts/topo-recent-row-hot-test.mjs +110 -0
- package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
- package/scripts/topo-recent-row-pin-test.mjs +126 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-title-test.mjs +112 -0
- package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
- package/scripts/topo-recent-row-ts-test.mjs +95 -0
- package/scripts/topo-recent-row-typography-test.mjs +119 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spokes-retired-test.mjs +93 -0
- package/scripts/topo-starfield-density-test.mjs +89 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -0
- package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
- package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
- package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
- package/scripts/topo-title-tracking-test.mjs +98 -0
- package/scripts/topo-titleblock-gap-test.mjs +86 -0
- package/scripts/topo-titleblock-margin-test.mjs +83 -0
- package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
- package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
- package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-view-smooth-test.mjs +131 -0
- package/scripts/topo-watermark-tracking-test.mjs +99 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_8snedeata9k.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0kwf..oq7.27x.js +0 -7
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/0sjqjenh7~k-j.js +0 -1
- package/.next/static/chunks/0y8v4grez.a76.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_buildManifest.js +0 -0
- /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Q3_3F7glo-M3CFrXA8POE → 3PMYuA9TBRow9F7QeNZc7}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* Round 31 verification: fleet-pressure chip with proportional segments.
|
|
2
|
+
* 4 working + 6 idle + 2 offline = 12 total → 33% / 50% / 17%. */
|
|
3
|
+
import { chromium } from 'playwright';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
7
|
+
const browser = await chromium.launch({ headless: true });
|
|
8
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
|
|
9
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
10
|
+
await ctx.addInitScript(() => {
|
|
11
|
+
try {
|
|
12
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
13
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
14
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
15
|
+
} catch {}
|
|
16
|
+
});
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
19
|
+
const r = await route.fetch();
|
|
20
|
+
const b = await r.json();
|
|
21
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
22
|
+
const make = (alias, status) => ({
|
|
23
|
+
alias, status, network_id: nid, project_dir: null,
|
|
24
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
25
|
+
});
|
|
26
|
+
const sessions = [
|
|
27
|
+
...Array.from({ length: 4 }, (_, i) => make(`w${i}`, 'working')),
|
|
28
|
+
...Array.from({ length: 6 }, (_, i) => make(`i${i}`, 'idle')),
|
|
29
|
+
...Array.from({ length: 2 }, (_, i) => make(`o${i}`, 'offline')),
|
|
30
|
+
];
|
|
31
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
32
|
+
});
|
|
33
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
34
|
+
const page = await ctx.newPage();
|
|
35
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
36
|
+
await page.waitForSelector('[data-fleet-pressure]', { timeout: 30000 });
|
|
37
|
+
await page.waitForTimeout(400);
|
|
38
|
+
|
|
39
|
+
const chip = '[data-fleet-pressure]';
|
|
40
|
+
const titleText = await page.locator(chip).getAttribute('title');
|
|
41
|
+
// The chip layout: <chip> > <label-span> + <bar-span> ; bar-span has the 3 segment spans.
|
|
42
|
+
const segments = await page.evaluate(() => {
|
|
43
|
+
const chip = document.querySelector('[data-fleet-pressure]');
|
|
44
|
+
if (!chip) return [];
|
|
45
|
+
// The bar container is the inline-flex w-16 span (second child).
|
|
46
|
+
const bar = [...chip.children].find(c => c.classList?.contains('w-16'));
|
|
47
|
+
if (!bar) return [];
|
|
48
|
+
return [...bar.children].map(el => ({ width: el.style.width, background: el.style.background }));
|
|
49
|
+
});
|
|
50
|
+
const segmentWidths = segments.map(s => s.width);
|
|
51
|
+
const segmentColors = segments.map(s => s.background);
|
|
52
|
+
|
|
53
|
+
// 4/12 = 33.33%, 6/12 = 50.00%, 2/12 = 16.67%
|
|
54
|
+
const close = (a, b) => Math.abs(parseFloat(a) - b) < 0.5;
|
|
55
|
+
|
|
56
|
+
await browser.close();
|
|
57
|
+
const results = {
|
|
58
|
+
titleHasCounts: titleText === '4 working · 6 idle · 2 offline',
|
|
59
|
+
threeSegments: segmentWidths.length === 3,
|
|
60
|
+
workingProportion: close(segmentWidths[0], 100 * 4 / 12),
|
|
61
|
+
idleProportion: close(segmentWidths[1], 100 * 6 / 12),
|
|
62
|
+
offlineProportion: close(segmentWidths[2], 100 * 2 / 12),
|
|
63
|
+
workingColor: /22c55e|rgb\(34, 197, 94\)/.test(segmentColors[0] || ''),
|
|
64
|
+
idleColor: /2dd4bf|rgb\(45, 212, 191\)/.test(segmentColors[1] || ''),
|
|
65
|
+
offlineColor: /6b7280|rgb\(107, 114, 128\)/.test(segmentColors[2] || ''),
|
|
66
|
+
};
|
|
67
|
+
const ok = Object.values(results).every(Boolean);
|
|
68
|
+
console.log(`${ok ? '✅' : '❌'} pressure bar:`, JSON.stringify(results), `widths=${segmentWidths.join(',')} colors=${segmentColors.join(' | ')}`);
|
|
69
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* Round 83 verification: pressure-bar segments expose a hover preview
|
|
2
|
+
* before the click pins. Matches the R55 legend + R79 status chip +
|
|
3
|
+
* R80 vendor letter hover pattern — same activeStatus mechanism, just
|
|
4
|
+
* the last surface that was click-only.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: 1 working + 2 idle + 1 offline. Hover the working segment →
|
|
7
|
+
* working node stays bright, idle + offline dim. Mouseleave → restore.
|
|
8
|
+
* Then click the working segment → pin sticks via boxShadow. Hovering
|
|
9
|
+
* idle while working is pinned → live preview switches; mouseleave
|
|
10
|
+
* falls back to the working pin (activeStatus = hoveredStatus ??
|
|
11
|
+
* pinnedStatus).
|
|
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: 1200 } });
|
|
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
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
26
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const sessions = [
|
|
37
|
+
{ alias: 'wkr', status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
38
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
39
|
+
{ alias: 'idl1', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
40
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
41
|
+
{ alias: 'idl2', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
42
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
43
|
+
{ alias: 'off', status: 'offline', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null, created_at: stale10, updated_at: stale10, last_seen_at: stale10 },
|
|
45
|
+
];
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(500);
|
|
54
|
+
|
|
55
|
+
const ops = () => page.evaluate(() => {
|
|
56
|
+
const o = {};
|
|
57
|
+
for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
|
|
58
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
59
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
60
|
+
}
|
|
61
|
+
return o;
|
|
62
|
+
});
|
|
63
|
+
const segShadow = (k) => page.evaluate((key) => {
|
|
64
|
+
const s = document.querySelector(`[data-pressure-seg="${key}"]`);
|
|
65
|
+
return s ? (s.getAttribute('style') || '').includes('inset') : false;
|
|
66
|
+
}, k);
|
|
67
|
+
|
|
68
|
+
const before = await ops();
|
|
69
|
+
|
|
70
|
+
// Hover working segment → working stays bright, others dim.
|
|
71
|
+
await page.locator('[data-pressure-seg="working"]').hover();
|
|
72
|
+
await page.waitForTimeout(250);
|
|
73
|
+
const onHoverWorking = await ops();
|
|
74
|
+
const hoverPinShadowBefore = await segShadow('working');
|
|
75
|
+
|
|
76
|
+
// Mouseleave → baseline restores (no pin yet).
|
|
77
|
+
await page.mouse.move(10, 10);
|
|
78
|
+
await page.waitForTimeout(250);
|
|
79
|
+
const afterLeave = await ops();
|
|
80
|
+
|
|
81
|
+
// Click to pin working — segment gets inset shadow.
|
|
82
|
+
await page.locator('[data-pressure-seg="working"]').click();
|
|
83
|
+
await page.mouse.move(10, 10);
|
|
84
|
+
await page.waitForTimeout(250);
|
|
85
|
+
const afterPin = await ops();
|
|
86
|
+
const pinShadow = await segShadow('working');
|
|
87
|
+
|
|
88
|
+
// Hover idle while working is pinned — hoveredStatus overrides for the
|
|
89
|
+
// duration of the hover. idle stays bright, working dims (transient).
|
|
90
|
+
await page.locator('[data-pressure-seg="idle"]').hover();
|
|
91
|
+
await page.waitForTimeout(250);
|
|
92
|
+
const onHoverIdle = await ops();
|
|
93
|
+
|
|
94
|
+
// Mouseleave → falls back to working pin (NOT cleared).
|
|
95
|
+
await page.mouse.move(10, 10);
|
|
96
|
+
await page.waitForTimeout(250);
|
|
97
|
+
const afterLeaveIdle = await ops();
|
|
98
|
+
|
|
99
|
+
await browser.close();
|
|
100
|
+
|
|
101
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
102
|
+
const dim = (v) => v != null && v < 0.4;
|
|
103
|
+
const results = {
|
|
104
|
+
before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2) && before.off <= 0.6,
|
|
105
|
+
hoverWorking_workerStays: bright(onHoverWorking.wkr),
|
|
106
|
+
hoverWorking_idlesDim: dim(onHoverWorking.idl1) && dim(onHoverWorking.idl2),
|
|
107
|
+
hoverWorking_offDim: dim(onHoverWorking.off),
|
|
108
|
+
hoverWorking_noPinYet: !hoverPinShadowBefore,
|
|
109
|
+
leave_restoresBaseline: bright(afterLeave.wkr) && bright(afterLeave.idl1) && bright(afterLeave.idl2),
|
|
110
|
+
pin_workerStays: bright(afterPin.wkr),
|
|
111
|
+
pin_idlesDim: dim(afterPin.idl1) && dim(afterPin.idl2),
|
|
112
|
+
pin_shadowRendered: pinShadow,
|
|
113
|
+
hoverIdle_transient: bright(onHoverIdle.idl1) && bright(onHoverIdle.idl2) && dim(onHoverIdle.wkr),
|
|
114
|
+
leaveIdle_pinPersists: bright(afterLeaveIdle.wkr) && dim(afterLeaveIdle.idl1) && dim(afterLeaveIdle.idl2),
|
|
115
|
+
};
|
|
116
|
+
const ok = Object.values(results).every(Boolean);
|
|
117
|
+
console.log(`${ok ? '✅' : '❌'} pressure-hover:`, JSON.stringify(results),
|
|
118
|
+
`\n before=`, before,
|
|
119
|
+
`\n onHoverWorking=`, onHoverWorking,
|
|
120
|
+
`\n afterPin=`, afterPin,
|
|
121
|
+
`\n onHoverIdle=`, onHoverIdle,
|
|
122
|
+
`\n afterLeaveIdle=`, afterLeaveIdle);
|
|
123
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Round 60 verification: each pressure-bar segment toggles a sticky
|
|
2
|
+
* status filter. Click "working" → all non-working nodes dim, working
|
|
3
|
+
* ones stay. Click again → release. Pin and the R55 legend hover feed
|
|
4
|
+
* the same node-opacity branch via `activeStatus = hoveredStatus ??
|
|
5
|
+
* pinnedStatus`, so hover transiently overrides without nuking the pin.
|
|
6
|
+
*
|
|
7
|
+
* Sessions:
|
|
8
|
+
* wkr — working
|
|
9
|
+
* idl1 — idle
|
|
10
|
+
* idl2 — idle
|
|
11
|
+
* off — offline
|
|
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
|
+
const stale = new Date(Date.now() - 30 * 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 sessions = [
|
|
36
|
+
{ alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
37
|
+
{ alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
38
|
+
{ alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
39
|
+
{ alias: 'off', status: 'offline', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
40
|
+
];
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(500);
|
|
49
|
+
|
|
50
|
+
const opacities = () => page.evaluate(() => {
|
|
51
|
+
const o = {};
|
|
52
|
+
for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
|
|
53
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
54
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
55
|
+
}
|
|
56
|
+
return o;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const before = await opacities();
|
|
60
|
+
|
|
61
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
62
|
+
const dim = (v) => v != null && v < 0.4;
|
|
63
|
+
|
|
64
|
+
// Click the WORKING segment.
|
|
65
|
+
const workingSeg = page.locator('[data-pressure-seg="working"]').first();
|
|
66
|
+
const workingExists = await workingSeg.count() > 0;
|
|
67
|
+
if (!workingExists) { console.log('❌ pressure segment[working] not found'); process.exit(1); }
|
|
68
|
+
await workingSeg.click({ force: true });
|
|
69
|
+
await page.waitForTimeout(250);
|
|
70
|
+
const afterPin = await opacities();
|
|
71
|
+
|
|
72
|
+
// Pin should persist past mouse moving away.
|
|
73
|
+
await page.mouse.move(10, 10);
|
|
74
|
+
await page.waitForTimeout(250);
|
|
75
|
+
const afterMouseMove = await opacities();
|
|
76
|
+
|
|
77
|
+
// Click again to release. R79 added hover-affordances on the working
|
|
78
|
+
// + online chips, so the click locator's mouse path can light a
|
|
79
|
+
// transient hoveredStatus en route to the pressure segment. Mouse-move
|
|
80
|
+
// to a neutral spot after the release click so we observe pin-only
|
|
81
|
+
// state (same gotcha R61's test documented inline).
|
|
82
|
+
await workingSeg.click({ force: true });
|
|
83
|
+
await page.mouse.move(10, 10);
|
|
84
|
+
await page.waitForTimeout(250);
|
|
85
|
+
const afterRelease = await opacities();
|
|
86
|
+
|
|
87
|
+
// Repeat for offline segment.
|
|
88
|
+
const offlineSeg = page.locator('[data-pressure-seg="offline"]').first();
|
|
89
|
+
await offlineSeg.click({ force: true });
|
|
90
|
+
await page.mouse.move(10, 10);
|
|
91
|
+
await page.waitForTimeout(250);
|
|
92
|
+
const afterOfflinePin = await opacities();
|
|
93
|
+
await offlineSeg.click({ force: true });
|
|
94
|
+
await page.mouse.move(10, 10);
|
|
95
|
+
await page.waitForTimeout(250);
|
|
96
|
+
const afterOfflineRelease = await opacities();
|
|
97
|
+
|
|
98
|
+
await browser.close();
|
|
99
|
+
|
|
100
|
+
const results = {
|
|
101
|
+
before_baseline: bright(before.wkr) && bright(before.idl1),
|
|
102
|
+
working_pinKeepsWorking: bright(afterPin.wkr),
|
|
103
|
+
working_pinDimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
|
|
104
|
+
working_pinDimsOffline: dim(afterPin.off),
|
|
105
|
+
working_pinSurvivesMouse: bright(afterMouseMove.wkr) && dim(afterMouseMove.idl1),
|
|
106
|
+
working_releaseRestoresAll: bright(afterRelease.wkr) && bright(afterRelease.idl1),
|
|
107
|
+
offline_pinKeepsOffline: bright(afterOfflinePin.off), // base offline 0.6 reads as bright (>=0.55)
|
|
108
|
+
offline_pinDimsOnline: dim(afterOfflinePin.wkr) && dim(afterOfflinePin.idl1),
|
|
109
|
+
offline_releaseRestoresAll: bright(afterOfflineRelease.wkr),
|
|
110
|
+
};
|
|
111
|
+
const ok = Object.values(results).every(Boolean);
|
|
112
|
+
console.log(`${ok ? '✅' : '❌'} pressure pin:`, JSON.stringify(results),
|
|
113
|
+
`\n before=`, before,
|
|
114
|
+
`\n afterPin=`, afterPin,
|
|
115
|
+
`\n afterMouseMove=`, afterMouseMove,
|
|
116
|
+
`\n afterRelease=`, afterRelease,
|
|
117
|
+
`\n afterOfflinePin=`, afterOfflinePin,
|
|
118
|
+
`\n afterOfflineRelease=`, afterOfflineRelease);
|
|
119
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* Round 210 verification: pressure-bar segments deepen their OWN
|
|
2
|
+
* colour on hover via filter: brightness(1.2). Closes the chip-row
|
|
3
|
+
* "hover deepens own identity" family at a 6th surface (the
|
|
4
|
+
* pressure-bar scope previously had R83 wire setHoveredStatus but
|
|
5
|
+
* the segment itself stayed flat).
|
|
6
|
+
*
|
|
7
|
+
* Pre-R210 hovering a segment fired R55 canvas dim via
|
|
8
|
+
* setHoveredStatus, but the segment's own background stayed at the
|
|
9
|
+
* unmodified tier color. R210 makes the cause element (segment)
|
|
10
|
+
* light up the same gesture it fires on the canvas (effect element).
|
|
11
|
+
*
|
|
12
|
+
* Test (cyber dark, 2 working + 1 idle + 1 offline → 3 segments):
|
|
13
|
+
* 1. 3 [data-pressure-seg] segments rendered.
|
|
14
|
+
* 2. Idle: every segment has hovered='false', filter='none' (or
|
|
15
|
+
* no filter style set).
|
|
16
|
+
* 3. Inline transition includes 'filter 150ms' alongside the
|
|
17
|
+
* existing R165 width 220ms + R60 box-shadow 150ms.
|
|
18
|
+
* 4. Hover working segment → working hovered='true', filter
|
|
19
|
+
* 'brightness(1.2)'.
|
|
20
|
+
* 5. Other (idle/offline) segments stay hovered='false', no filter.
|
|
21
|
+
* 6. Move away → working back to hovered='false', no filter.
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias, status) => ({
|
|
41
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('alpha', 'working'),
|
|
47
|
+
mk('beta', 'working'),
|
|
48
|
+
mk('gamma', 'idle'),
|
|
49
|
+
mk('delta', 'offline'),
|
|
50
|
+
] } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-pressure-seg="working"]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(300);
|
|
60
|
+
|
|
61
|
+
const probe = async () => page.evaluate(() => {
|
|
62
|
+
return [...document.querySelectorAll('[data-pressure-seg]')].map(el => {
|
|
63
|
+
const cs = getComputedStyle(el);
|
|
64
|
+
return {
|
|
65
|
+
key: el.getAttribute('data-pressure-seg'),
|
|
66
|
+
hovered: el.getAttribute('data-pressure-seg-hovered'),
|
|
67
|
+
filter: cs.filter,
|
|
68
|
+
inlineFilter: el.style.filter,
|
|
69
|
+
transition: el.style.transition,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const idle = await probe();
|
|
75
|
+
|
|
76
|
+
await page.hover('[data-pressure-seg="working"]');
|
|
77
|
+
await page.waitForTimeout(180);
|
|
78
|
+
const hovered = await probe();
|
|
79
|
+
|
|
80
|
+
await page.mouse.move(0, 0);
|
|
81
|
+
await page.waitForTimeout(180);
|
|
82
|
+
const settled = await probe();
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const idleWorking = idle.find(s => s.key === 'working');
|
|
87
|
+
const idleIdle = idle.find(s => s.key === 'idle');
|
|
88
|
+
const idleOffline = idle.find(s => s.key === 'offline');
|
|
89
|
+
const hovWorking = hovered.find(s => s.key === 'working');
|
|
90
|
+
const hovIdle = hovered.find(s => s.key === 'idle');
|
|
91
|
+
const settledWorking = settled.find(s => s.key === 'working');
|
|
92
|
+
|
|
93
|
+
const isNoFilter = (s) => !s || s === 'none' || s === '';
|
|
94
|
+
const hasBrightness = (s) => /brightness\s*\(\s*1\.2/.test(s || '');
|
|
95
|
+
|
|
96
|
+
const results = {
|
|
97
|
+
three_segments_present: idle.length === 3,
|
|
98
|
+
working_found: !!idleWorking,
|
|
99
|
+
idle_found: !!idleIdle,
|
|
100
|
+
offline_found: !!idleOffline,
|
|
101
|
+
|
|
102
|
+
// Idle: no filter, hovered='false', transitions present
|
|
103
|
+
idle_all_not_hovered: idle.every(s => s.hovered === 'false'),
|
|
104
|
+
idle_all_no_filter: idle.every(s => isNoFilter(s.inlineFilter)),
|
|
105
|
+
transitions_have_filter: idle.every(s => /filter\s+(?:150ms|0\.15s)/.test(s.transition || '')),
|
|
106
|
+
transitions_keep_width: idle.every(s => /width\s+(?:220ms|0\.22s)/.test(s.transition || '')),
|
|
107
|
+
transitions_keep_bs: idle.every(s => /box-shadow\s+(?:150ms|0\.15s)/.test(s.transition || '')),
|
|
108
|
+
|
|
109
|
+
// Hover working: brightness applied
|
|
110
|
+
hov_working_hovered_true: hovWorking?.hovered === 'true',
|
|
111
|
+
hov_working_has_filter: hasBrightness(hovWorking?.inlineFilter)
|
|
112
|
+
|| hasBrightness(hovWorking?.filter),
|
|
113
|
+
// Other segments stay idle
|
|
114
|
+
hov_idle_stays_false: hovIdle?.hovered === 'false',
|
|
115
|
+
hov_idle_no_filter: isNoFilter(hovIdle?.inlineFilter),
|
|
116
|
+
|
|
117
|
+
// Settled: back to idle
|
|
118
|
+
settled_hovered_false: settledWorking?.hovered === 'false',
|
|
119
|
+
settled_no_filter: isNoFilter(settledWorking?.inlineFilter),
|
|
120
|
+
};
|
|
121
|
+
const ok = Object.values(results).every(Boolean);
|
|
122
|
+
console.log(`${ok ? '✅' : '❌'} pressure-seg self-hover:`, JSON.stringify(results),
|
|
123
|
+
`\n idle:`, idle,
|
|
124
|
+
`\n hovered:`, hovered,
|
|
125
|
+
`\n settled:`, settled);
|
|
126
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* Round 102 verification: pressure-bar segment tooltips list the
|
|
2
|
+
* aliases that fall into that status bucket. Closes the last "how
|
|
3
|
+
* many but not which" gap in the chip-row surfaces. Composes with
|
|
4
|
+
* the existing R83 hover preview (canvas dims still answer it
|
|
5
|
+
* visually too) — this adds a screen-reader-friendly textual
|
|
6
|
+
* channel for the same info.
|
|
7
|
+
*
|
|
8
|
+
* Fleet: 2 working + 2 idle + 1 offline. Verify each segment's
|
|
9
|
+
* title contains the matching aliases + action hint; data-pressure-
|
|
10
|
+
* seg-aliases attr carries the full CSV.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
23
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
24
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
25
|
+
} catch {}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias, status, ts) => ({
|
|
35
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: ts, updated_at: ts, last_seen_at: ts,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
40
|
+
mk('wkr1', 'working', fresh),
|
|
41
|
+
mk('wkr2', 'working', fresh),
|
|
42
|
+
mk('idl1', 'idle', fresh),
|
|
43
|
+
mk('idl2', 'idle', fresh),
|
|
44
|
+
mk('off1', 'offline', stale10),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const read = (key) => page.evaluate((k) => {
|
|
55
|
+
const el = document.querySelector(`[data-pressure-seg="${k}"]`);
|
|
56
|
+
if (!el) return null;
|
|
57
|
+
return {
|
|
58
|
+
title: el.getAttribute('title') || '',
|
|
59
|
+
aliases: el.getAttribute('data-pressure-seg-aliases') || '',
|
|
60
|
+
};
|
|
61
|
+
}, key);
|
|
62
|
+
|
|
63
|
+
const working = await read('working');
|
|
64
|
+
const idle = await read('idle');
|
|
65
|
+
const offline = await read('offline');
|
|
66
|
+
|
|
67
|
+
// Pin working — action hint should flip in the title.
|
|
68
|
+
await page.locator('[data-pressure-seg="working"]').click();
|
|
69
|
+
await page.waitForTimeout(250);
|
|
70
|
+
const workingPinned = await read('working');
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const sameSet = (csv, expected) => {
|
|
75
|
+
const got = new Set(csv.split(',').filter(Boolean));
|
|
76
|
+
const exp = new Set(expected);
|
|
77
|
+
return got.size === exp.size && [...exp].every(x => got.has(x));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
working_aliasesMatch: sameSet(working?.aliases || '', ['wkr1', 'wkr2']),
|
|
82
|
+
working_titleListsThem: /wkr1.*wkr2|wkr2.*wkr1/.test(working?.title || ''),
|
|
83
|
+
working_titleClickHint: /click to highlight/.test(working?.title || ''),
|
|
84
|
+
working_titleStatusN: /^2 working/m.test(working?.title || ''),
|
|
85
|
+
idle_aliasesMatch: sameSet(idle?.aliases || '', ['idl1', 'idl2']),
|
|
86
|
+
idle_titleListsThem: /idl1.*idl2|idl2.*idl1/.test(idle?.title || ''),
|
|
87
|
+
offline_aliasesMatch: sameSet(offline?.aliases || '', ['off1']),
|
|
88
|
+
offline_titleListsThem: /off1/.test(offline?.title || ''),
|
|
89
|
+
workingPin_aliasesStable: sameSet(workingPinned?.aliases || '', ['wkr1', 'wkr2']),
|
|
90
|
+
workingPin_hintFlipped: /click to release filter/.test(workingPinned?.title || ''),
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} pressure-seg tooltip:`, JSON.stringify(results),
|
|
94
|
+
`\n working:`, working,
|
|
95
|
+
`\n idle:`, idle,
|
|
96
|
+
`\n offline:`, offline,
|
|
97
|
+
`\n workingPinned:`, workingPinned);
|
|
98
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 165 verification: pressure-bar segments smoothly
|
|
2
|
+
* transition their widths.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R165 segment widths snapped instantly when fleet
|
|
5
|
+
* composition shifted (e.g., a node going idle → working).
|
|
6
|
+
* R165 adds `transition: width 220ms ease-out, box-shadow
|
|
7
|
+
* 150ms ease-out` on each <span data-pressure-seg> inline
|
|
8
|
+
* style — smooths the breath, doesn't change semantics.
|
|
9
|
+
*
|
|
10
|
+
* Tests:
|
|
11
|
+
* 1. All segments carry the inline transition declaration
|
|
12
|
+
* 2. Pin one segment → boxShadow string changes (R60 idiom)
|
|
13
|
+
* 3. Width attribute is a percentage of (n/total)*100
|
|
14
|
+
*/
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
|
|
21
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
22
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
23
|
+
await ctx.addInitScript(() => {
|
|
24
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
28
|
+
const stale = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const mk = (alias, status, last) => ({
|
|
34
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
35
|
+
network_id: nid, project_dir: null,
|
|
36
|
+
created_at: last, updated_at: last, last_seen_at: last,
|
|
37
|
+
});
|
|
38
|
+
// 3 working + 2 idle + 1 offline = 6 total
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
40
|
+
mk('a1', 'working', fresh), mk('a2', 'working', fresh), mk('a3', 'working', fresh),
|
|
41
|
+
mk('b1', 'idle', fresh), mk('b2', 'idle', fresh),
|
|
42
|
+
mk('c1', 'offline', stale),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
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 === 6, { timeout: 30000 });
|
|
51
|
+
await page.waitForSelector('[data-pressure-seg]', { timeout: 10000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const probe = await page.evaluate(() => {
|
|
55
|
+
const segs = ['working', 'idle', 'offline'].map(k => {
|
|
56
|
+
const el = document.querySelector(`[data-pressure-seg="${k}"]`);
|
|
57
|
+
if (!el) return null;
|
|
58
|
+
// Inline style is what we wrote; window.getComputedStyle reports the
|
|
59
|
+
// resolved value (which for `transition` is the long form).
|
|
60
|
+
const inlineTransition = el.style.transition || '';
|
|
61
|
+
const computedTransition = getComputedStyle(el).transition || '';
|
|
62
|
+
return {
|
|
63
|
+
key: k,
|
|
64
|
+
width: el.style.width,
|
|
65
|
+
inlineTransition,
|
|
66
|
+
// Match either width 220ms OR the resolved computed form.
|
|
67
|
+
hasTransitionWidth:
|
|
68
|
+
inlineTransition.includes('width 220ms') ||
|
|
69
|
+
/width\s+0\.22s|width\s+220ms/.test(computedTransition),
|
|
70
|
+
hasTransitionBoxShadow:
|
|
71
|
+
inlineTransition.includes('box-shadow 150ms') ||
|
|
72
|
+
/box-shadow\s+0\.15s|box-shadow\s+150ms/.test(computedTransition),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
return segs;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Pin the idle segment → boxShadow should appear (R60 inset rings)
|
|
79
|
+
const boxBeforePin = await page.evaluate(() => {
|
|
80
|
+
const el = document.querySelector('[data-pressure-seg="idle"]');
|
|
81
|
+
return el ? el.style.boxShadow : null;
|
|
82
|
+
});
|
|
83
|
+
await page.locator('[data-pressure-seg="idle"]').click({ force: true });
|
|
84
|
+
await page.waitForTimeout(300);
|
|
85
|
+
const boxAfterPin = await page.evaluate(() => {
|
|
86
|
+
const el = document.querySelector('[data-pressure-seg="idle"]');
|
|
87
|
+
return el ? el.style.boxShadow : null;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const allOk = probe.every(p => p && p.hasTransitionWidth && p.hasTransitionBoxShadow);
|
|
93
|
+
const results = {
|
|
94
|
+
three_segs_present: probe.length === 3 && probe.every(p => p !== null),
|
|
95
|
+
all_have_transition_width: probe.every(p => p?.hasTransitionWidth),
|
|
96
|
+
all_have_transition_boxShadow: probe.every(p => p?.hasTransitionBoxShadow),
|
|
97
|
+
working_width_50: probe[0]?.width === '50%', // 3/6
|
|
98
|
+
idle_width_33: /33\.3|33%/.test(probe[1]?.width || ''), // 2/6
|
|
99
|
+
offline_width_16: /16\.6|16%/.test(probe[2]?.width || ''), // 1/6
|
|
100
|
+
pin_changes_boxShadow: boxBeforePin === '' && boxAfterPin !== '',
|
|
101
|
+
overall: allOk,
|
|
102
|
+
};
|
|
103
|
+
const ok = Object.values(results).every(Boolean);
|
|
104
|
+
console.log(`${ok ? '✅' : '❌'} pressure-seg transition:`, JSON.stringify(results),
|
|
105
|
+
`\n probes=`, probe,
|
|
106
|
+
`\n box before/after pin =`, JSON.stringify(boxBeforePin), '→', JSON.stringify(boxAfterPin));
|
|
107
|
+
process.exit(ok ? 0 : 1);
|