@skyhook-io/radar-app 1.13.5 → 1.14.1
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/package.json +9 -9
- package/src/App.tsx +66 -28
- package/src/api/client.authRedirect.test.ts +12 -1
- package/src/api/client.prometheusStatus.test.ts +24 -0
- package/src/api/client.ts +324 -16
- package/src/api/diagnose.ts +32 -1
- package/src/api/drain-plan.test.ts +86 -0
- package/src/api/quotas.test.ts +26 -0
- package/src/api/quotas.ts +20 -0
- package/src/api/workloadMetrics.test.ts +40 -0
- package/src/api/workloadMetrics.ts +68 -0
- package/src/components/CloudConnectFlow.tsx +343 -46
- package/src/components/CloudFunnelButton.tsx +110 -37
- package/src/components/ConnectionErrorView.tsx +9 -10
- package/src/components/DebugOverlay.tsx +10 -6
- package/src/components/SyncProgressPanel.tsx +73 -0
- package/src/components/audit/AuditView.tsx +2 -0
- package/src/components/cloudConnectHandoff.test.ts +245 -0
- package/src/components/cloudConnectHandoff.ts +276 -0
- package/src/components/curl/ServiceCurlButton.tsx +19 -26
- package/src/components/diagnose/AgentCase.tsx +18 -5
- package/src/components/diagnose/AnalysisStory.test.tsx +308 -0
- package/src/components/diagnose/AnalysisStory.tsx +564 -0
- package/src/components/diagnose/DiagnoseContext.test.ts +10 -0
- package/src/components/diagnose/DiagnoseContext.tsx +26 -8
- package/src/components/diagnose/DiagnoseSurface.tsx +20 -14
- package/src/components/diagnose/InvestigationEvidencePane.story.test.tsx +771 -0
- package/src/components/diagnose/InvestigationEvidencePane.test.tsx +8 -33
- package/src/components/diagnose/InvestigationEvidencePane.tsx +809 -168
- package/src/components/diagnose/InvestigationResourceEvidence.test.tsx +30 -0
- package/src/components/diagnose/InvestigationResourceEvidence.tsx +20 -0
- package/src/components/diagnose/InvestigationView.tsx +493 -529
- package/src/components/diagnose/investigationCase.test.tsx +267 -129
- package/src/components/diagnose/investigationCase.ts +122 -77
- package/src/components/diagnose/investigationEvidence/adapters/changes.ts +60 -0
- package/src/components/diagnose/investigationEvidence/adapters/diagnose.test.ts +573 -0
- package/src/components/diagnose/investigationEvidence/adapters/diagnose.ts +935 -0
- package/src/components/diagnose/investigationEvidence/adapters/events.ts +96 -0
- package/src/components/diagnose/investigationEvidence/adapters/helm.test.ts +242 -0
- package/src/components/diagnose/investigationEvidence/adapters/helm.ts +208 -0
- package/src/components/diagnose/investigationEvidence/adapters/issues.ts +172 -0
- package/src/components/diagnose/investigationEvidence/adapters/logs.test.ts +283 -0
- package/src/components/diagnose/investigationEvidence/adapters/logs.ts +183 -0
- package/src/components/diagnose/investigationEvidence/adapters/permissions.test.ts +528 -0
- package/src/components/diagnose/investigationEvidence/adapters/permissions.ts +292 -0
- package/src/components/diagnose/investigationEvidence/adapters/prometheus.test.ts +1641 -0
- package/src/components/diagnose/investigationEvidence/adapters/prometheus.ts +769 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.test.ts +81 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.ts +285 -0
- package/src/components/diagnose/investigationEvidence/adapters/topology.ts +207 -0
- package/src/components/diagnose/investigationEvidence/builder.ts +427 -0
- package/src/components/diagnose/investigationEvidence/evidenceFixtures.ts +179 -0
- package/src/components/diagnose/investigationEvidence/identity.test.ts +632 -0
- package/src/components/diagnose/investigationEvidence/identity.ts +261 -0
- package/src/components/diagnose/investigationEvidence/index.ts +55 -0
- package/src/components/diagnose/investigationEvidence/observations.ts +978 -0
- package/src/components/diagnose/investigationEvidence/parse.ts +467 -0
- package/src/components/diagnose/investigationEvidence/projection.test.ts +2736 -0
- package/src/components/diagnose/investigationEvidence/types.ts +514 -0
- package/src/components/diagnose/investigationEvidencePresentation.test.ts +2 -0
- package/src/components/diagnose/investigationEvidencePresentation.ts +3 -0
- package/src/components/diagnose/investigationState.test.ts +316 -58
- package/src/components/diagnose/investigationState.ts +257 -44
- package/src/components/diagnose/investigationStory.test.ts +161 -0
- package/src/components/diagnose/investigationStory.ts +207 -0
- package/src/components/diagnose/parts.test.tsx +9 -6
- package/src/components/diagnose/parts.tsx +977 -193
- package/src/components/diagnose/storyParts.test.tsx +426 -0
- package/src/components/diagnose/target.test.ts +19 -0
- package/src/components/diagnose/target.ts +3 -2
- package/src/components/diagnose/toolCallLabel.test.ts +51 -0
- package/src/components/diagnose/toolCallLabel.ts +135 -0
- package/src/components/diagnose/useDisclosureReveal.ts +10 -11
- package/src/components/gitops/GitOpsView.tsx +7 -0
- package/src/components/gitops/RemoteDestinationCloudHint.tsx +26 -0
- package/src/components/helm/HelmCompareRoute.tsx +18 -4
- package/src/components/helm/HelmReleaseDrawer.tsx +11 -26
- package/src/components/helm/InstallWizard.tsx +8 -3
- package/src/components/home/MCPSetupDialog.tsx +15 -9
- package/src/components/portforward/PortForwardManager.tsx +6 -13
- package/src/components/resource/PrometheusCharts.overlays.test.ts +22 -0
- package/src/components/resource/PrometheusCharts.tsx +4 -3
- package/src/components/resource/PrometheusChartsGrid.render.test.tsx +99 -0
- package/src/components/resource/PrometheusChartsGrid.tsx +75 -35
- package/src/components/resource/RightsizingStrip.test.ts +38 -0
- package/src/components/resource/RightsizingStrip.tsx +15 -3
- package/src/components/resource/WorkloadMetricsHelpDialog.test.tsx +79 -0
- package/src/components/resource/WorkloadMetricsHelpDialog.tsx +145 -0
- package/src/components/resource/WorkloadMetricsSection.render.test.tsx +351 -0
- package/src/components/resource/WorkloadMetricsSection.tsx +480 -0
- package/src/components/resource/workloadMetricValues.test.ts +65 -0
- package/src/components/resource/workloadMetricValues.ts +30 -0
- package/src/components/resources/PodFilePreview.tsx +3 -3
- package/src/components/resources/ResourcesView.tsx +44 -6
- package/src/components/resources/renderers/NamespaceRenderer.tsx +7 -1
- package/src/components/resources/renderers/PodRenderer.tsx +4 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +15 -1
- package/src/components/resources/renderers/ServiceRenderer.tsx +2 -1
- package/src/components/resources/renderers/WorkloadRenderer.tsx +3 -0
- package/src/components/rightsizing/RightsizingScanView.tsx +21 -0
- package/src/components/settings/SettingsDialog.tsx +98 -16
- package/src/components/traffic/TrafficFlowList.tsx +229 -70
- package/src/components/traffic/TrafficGraph.tsx +6 -6
- package/src/components/traffic/TrafficView.tsx +10 -9
- package/src/components/traffic/trafficFilters.test.ts +28 -1
- package/src/components/traffic/trafficFilters.ts +31 -0
- package/src/components/ui/CommandPalette.tsx +10 -6
- package/src/components/ui/DiagnosticsOverlay.tsx +10 -6
- package/src/components/ui/Disclosure.tsx +47 -0
- package/src/components/ui/Markdown.tsx +23 -11
- package/src/components/ui/ShortcutHelpOverlay.tsx +3 -1
- package/src/components/ui/UpdateNotification.tsx +18 -2
- package/src/components/workload/WorkloadView.tsx +19 -8
- package/src/context/ConnectionContext.tsx +48 -3
- package/src/context/DiagnoseCustomization.tsx +1 -0
- package/src/index.css +36 -6
- package/src/utils/navigation.ts +1 -1
- package/src/utils/topology-namespace.test.ts +64 -0
- package/src/utils/topology-namespace.ts +18 -0
- package/src/components/diagnose/investigationEvidence.test.ts +0 -6617
- package/src/components/diagnose/investigationEvidence.ts +0 -5572
- package/src/components/gitops/GitOpsActions.tsx +0 -1
- package/src/components/gitops/GitOpsStatusBadge.tsx +0 -1
- package/src/components/gitops/ManagedResourcesList.tsx +0 -1
- package/src/components/gitops/SyncCountdown.tsx +0 -1
- package/src/components/gitops/index.ts +0 -4
- package/src/components/helm/ManifestDiffViewer.tsx +0 -69
- package/src/components/logs/JsonLogLine.tsx +0 -1
- package/src/components/logs/LogCore.tsx +0 -2
- package/src/components/logs/useLogBuffer.ts +0 -2
- package/src/components/logs/useLogSearch.ts +0 -1
- package/src/components/resource-drawer/ResourceDrawer.tsx +0 -216
- package/src/components/resources/drawer-components.tsx +0 -1
- package/src/components/resources/renderers/AlertRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +0 -1
- package/src/components/resources/renderers/CertificateRenderer.tsx +0 -1
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +0 -1
- package/src/components/resources/renderers/ChallengeRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +0 -1
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +0 -1
- package/src/components/resources/renderers/CronJobRenderer.tsx +0 -1
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +0 -1
- package/src/components/resources/renderers/EventRenderer.tsx +0 -1
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +0 -1
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/GatewayRenderer.tsx +0 -1
- package/src/components/resources/renderers/GenericRenderer.tsx +0 -1
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/IngressClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/IngressRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +0 -1
- package/src/components/resources/renderers/JobRenderer.tsx +0 -1
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +0 -1
- package/src/components/resources/renderers/KustomizationRenderer.tsx +0 -1
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/LeaseRenderer.tsx +0 -1
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +0 -1
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/OrderRenderer.tsx +0 -1
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +0 -1
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +0 -1
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +0 -1
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +0 -1
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +0 -1
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/SbomReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/SecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +0 -1
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +0 -1
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/StorageClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/VPARenderer.tsx +0 -1
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +0 -1
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +0 -1
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +0 -1
- package/src/components/resources/renderers/WorkflowRenderer.tsx +0 -1
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +0 -1
- package/src/components/resources/renderers/argo-cells.tsx +0 -1
- package/src/components/resources/renderers/certmanager-cells.tsx +0 -1
- package/src/components/resources/renderers/cnpg-cells.tsx +0 -1
- package/src/components/resources/renderers/eso-cells.tsx +0 -1
- package/src/components/resources/renderers/flux-cells.tsx +0 -1
- package/src/components/resources/renderers/index.ts +0 -94
- package/src/components/resources/renderers/istio-cells.tsx +0 -1
- package/src/components/resources/renderers/karpenter-cells.tsx +0 -1
- package/src/components/resources/renderers/keda-cells.tsx +0 -1
- package/src/components/resources/renderers/knative-cells.tsx +0 -1
- package/src/components/resources/renderers/kyverno-cells.tsx +0 -1
- package/src/components/resources/renderers/prometheus-cells.tsx +0 -1
- package/src/components/resources/renderers/traefik-cells.tsx +0 -1
- package/src/components/resources/renderers/trivy-cells.tsx +0 -1
- package/src/components/resources/renderers/trivy-shared.tsx +0 -1
- package/src/components/resources/renderers/velero-cells.tsx +0 -1
- package/src/components/resources/resource-utils-argo.ts +0 -2
- package/src/components/resources/resource-utils-certmanager.ts +0 -2
- package/src/components/resources/resource-utils-eso.ts +0 -2
- package/src/components/resources/resource-utils-flux.ts +0 -2
- package/src/components/resources/resource-utils-istio.ts +0 -2
- package/src/components/resources/resource-utils-karpenter.ts +0 -2
- package/src/components/resources/resource-utils-knative.ts +0 -2
- package/src/components/resources/resource-utils-kyverno.ts +0 -2
- package/src/components/resources/resource-utils-prometheus.ts +0 -2
- package/src/components/resources/resource-utils-traefik.ts +0 -1
- package/src/components/resources/resource-utils-trivy.ts +0 -2
- package/src/components/resources/resource-utils-velero.ts +0 -2
- package/src/components/shared/EditableYamlView.tsx +0 -24
- package/src/components/timeline/DiffViewer.tsx +0 -1
- package/src/components/traffic/index.ts +0 -3
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +0 -1
- package/src/components/ui/MetricsChart.tsx +0 -1
- package/src/components/ui/ResourceBar.tsx +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/radar-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.1",
|
|
4
4
|
"description": "Radar's full web UI as a reusable React component. Used by Radar's own binary and by external consumers like Radar Cloud.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"lint": "eslint src && node scripts/check-no-native-title.mjs"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@fontsource-variable/dm-sans": "^5.
|
|
32
|
+
"@fontsource-variable/dm-sans": "^5.3.0",
|
|
33
33
|
"@fontsource/dm-mono": "^5.3.0",
|
|
34
34
|
"@monaco-editor/react": "^4.7.0",
|
|
35
35
|
"diff": "^9.0.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"yaml": "^2.9.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@skyhook-io/k8s-ui": ">=1.
|
|
44
|
+
"@skyhook-io/k8s-ui": ">=1.15.0",
|
|
45
45
|
"@tanstack/react-query": ">=5",
|
|
46
46
|
"@xyflow/react": ">=12.0.0",
|
|
47
47
|
"clsx": ">=2",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"@tailwindcss/vite": "^4.3.3",
|
|
61
61
|
"@tanstack/react-query": "^5.102.8",
|
|
62
62
|
"@types/node": "^26.2.0",
|
|
63
|
-
"@types/react": "^19.
|
|
64
|
-
"@types/react-dom": "^19.
|
|
65
|
-
"@vitejs/plugin-react": "^6.1.
|
|
63
|
+
"@types/react": "^19.3.0",
|
|
64
|
+
"@types/react-dom": "^19.3.0",
|
|
65
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
66
66
|
"@xyflow/react": "^12.11.2",
|
|
67
67
|
"clsx": "^2.1.1",
|
|
68
68
|
"elkjs": "^0.11.1",
|
|
@@ -73,13 +73,13 @@
|
|
|
73
73
|
"lucide-react": "^1.37.0",
|
|
74
74
|
"postcss": "^8.5.28",
|
|
75
75
|
"prettier": "^3.9.5",
|
|
76
|
-
"react": "^19.
|
|
77
|
-
"react-dom": "^19.
|
|
76
|
+
"react": "^19.3.0",
|
|
77
|
+
"react-dom": "^19.3.0",
|
|
78
78
|
"react-router-dom": "^7.18.3",
|
|
79
79
|
"tailwind-merge": "^3.6.0",
|
|
80
80
|
"tailwindcss": "^4.3.3",
|
|
81
81
|
"typescript": "^6.0.2",
|
|
82
|
-
"typescript-eslint": "^8.
|
|
82
|
+
"typescript-eslint": "^8.70.0",
|
|
83
83
|
"vite": "^8.2.2"
|
|
84
84
|
},
|
|
85
85
|
"sideEffects": [
|
package/src/App.tsx
CHANGED
|
@@ -30,7 +30,7 @@ import { ApplicationsView } from './components/applications/ApplicationsView'
|
|
|
30
30
|
import { HelmReleaseDrawer } from './components/helm/HelmReleaseDrawer'
|
|
31
31
|
import { PortForwardProvider, PortForwardIndicator, PortForwardPanel } from './components/portforward/PortForwardManager'
|
|
32
32
|
import { DockProvider, BottomDock, useDock, useDockReservedHeight, useOpenLocalTerminal } from './components/dock'
|
|
33
|
-
import { DURATION_DOCK } from '@skyhook-io/k8s-ui/utils/animation'
|
|
33
|
+
import { DURATION_DOCK, overlayExitMs } from '@skyhook-io/k8s-ui/utils/animation'
|
|
34
34
|
import { ContextSwitcher } from './components/ContextSwitcher'
|
|
35
35
|
import { NamespaceSwitcher, type NamespaceSwitcherHandle } from './components/NamespaceSwitcher'
|
|
36
36
|
import { CloudFunnelButton } from './components/CloudFunnelButton'
|
|
@@ -43,6 +43,7 @@ import { useMediaQuery } from './hooks/useMediaQuery'
|
|
|
43
43
|
import { ContextSwitchProvider, useContextSwitch } from './context/ContextSwitchContext'
|
|
44
44
|
import { ConnectionProvider, useConnection } from './context/ConnectionContext'
|
|
45
45
|
import { ConnectionErrorView } from './components/ConnectionErrorView'
|
|
46
|
+
import { SyncProgressPanel } from './components/SyncProgressPanel'
|
|
46
47
|
import { CapabilitiesProvider, useCapabilitiesContext } from './contexts/CapabilitiesContext'
|
|
47
48
|
import { UserMenu } from './components/UserMenu'
|
|
48
49
|
import { ErrorBoundary } from './components/ui/ErrorBoundary'
|
|
@@ -53,6 +54,7 @@ import { DiagnosticsOverlay } from './components/ui/DiagnosticsOverlay'
|
|
|
53
54
|
import { useEventSource } from './hooks/useEventSource'
|
|
54
55
|
import { debugNamespaceLog, useNamespaces, useNamespaceScope, useSetActiveNamespace, useSwitchContext, useAuthMe, useAudit } from './api/client'
|
|
55
56
|
import { buildAuditSeverityMap } from './utils/auditBadges'
|
|
57
|
+
import { isInNamespaceScope, scopeNodesToNamespaces } from './utils/topology-namespace'
|
|
56
58
|
import { routePath, apiUrl, getAuthHeaders, getCredentialsMode, stripBasename } from './api/config'
|
|
57
59
|
import { KeyboardShortcutProvider, useRegisterShortcut, useRegisterShortcuts, useSuppressBaseShortcuts } from './hooks/useKeyboardShortcuts'
|
|
58
60
|
import { useAnimatedUnmount } from './hooks/useAnimatedUnmount'
|
|
@@ -678,9 +680,11 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
678
680
|
// Animation hooks for smooth mount/unmount transitions
|
|
679
681
|
const resourceDrawer = useAnimatedUnmount(!!routeSelectedResource, 300)
|
|
680
682
|
const helmDrawer = useAnimatedUnmount(!!(mainView === 'helm' && selectedHelmRelease), 300)
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const
|
|
683
|
+
// Dialog-kind overlays wait their exit duration (shorter than the entrance);
|
|
684
|
+
// drawers keep the symmetric slide.
|
|
685
|
+
const helpOverlay = useAnimatedUnmount(showHelp, overlayExitMs('dialog'))
|
|
686
|
+
const commandPaletteAnim = useAnimatedUnmount(showCommandPalette, overlayExitMs('dialog'))
|
|
687
|
+
const diagnosticsOverlay = useAnimatedUnmount(showDiagnostics, overlayExitMs('dialog'))
|
|
684
688
|
|
|
685
689
|
// Hold last valid values so drawers can animate out before data disappears
|
|
686
690
|
const lastResourceRef = useRef(routeSelectedResource)
|
|
@@ -992,6 +996,15 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
992
996
|
const contentReady = !isSwitching && !authMePending &&
|
|
993
997
|
!(authMe?.authEnabled && !authMe?.username) && connection.state === 'connected'
|
|
994
998
|
|
|
999
|
+
// Progressive shell during the initial informer sync: once the server
|
|
1000
|
+
// publishes per-kind sync progress, render the app instead of the splash.
|
|
1001
|
+
// Resource views serve kinds as they become ready; everything else shows
|
|
1002
|
+
// the sync progress panel until 'connected'.
|
|
1003
|
+
const shellDuringSync = !isSwitching && !authMePending &&
|
|
1004
|
+
!(authMe?.authEnabled && !authMe?.username) &&
|
|
1005
|
+
connection.state === 'connecting' && !!connection.syncStatus?.kinds?.length
|
|
1006
|
+
const viewsSyncGated = shellDuringSync && mainView !== 'resources'
|
|
1007
|
+
|
|
995
1008
|
const { clusterLoadState, showHomeClusterLoadFallback, clusterLoadInitial } = useClusterLoadState({
|
|
996
1009
|
namespaces,
|
|
997
1010
|
mainView,
|
|
@@ -1245,16 +1258,22 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
1245
1258
|
const clusterConnectionState = connection.state
|
|
1246
1259
|
const clusterConnected = clusterConnectionState === 'connected'
|
|
1247
1260
|
const liveUpdatesDisconnected = clusterConnected && !eventStreamConnected && !eventStreamConnecting
|
|
1261
|
+
// During the progressive shell, the header label carries the global sync
|
|
1262
|
+
// progress — the one place that explains why some views are open and
|
|
1263
|
+
// others still loading.
|
|
1264
|
+
const syncProgressLabel = connection.syncStatus
|
|
1265
|
+
? `Loading cluster data — ${connection.syncStatus.criticalSynced + connection.syncStatus.deferredSynced} of ${connection.syncStatus.criticalTotal + connection.syncStatus.deferredTotal} ready`
|
|
1266
|
+
: 'Connecting'
|
|
1248
1267
|
const headerConnectionLabel =
|
|
1249
1268
|
clusterConnectionState === 'disconnected' ? 'Disconnected' :
|
|
1250
|
-
clusterConnectionState === 'connecting' ?
|
|
1269
|
+
clusterConnectionState === 'connecting' ? syncProgressLabel :
|
|
1251
1270
|
liveUpdatesDisconnected ? 'Live updates disconnected' :
|
|
1252
1271
|
clusterLoadState.loading ? `Connected — ${clusterLoadState.message}` :
|
|
1253
1272
|
crdDiscoveryStatus === 'discovering' ? 'Connected — discovering Custom Resources...' :
|
|
1254
1273
|
'Connected'
|
|
1255
1274
|
const headerConnectionDisplayLabel =
|
|
1256
1275
|
clusterConnectionState === 'disconnected' ? 'Disconnected' :
|
|
1257
|
-
clusterConnectionState === 'connecting' ?
|
|
1276
|
+
clusterConnectionState === 'connecting' ? syncProgressLabel :
|
|
1258
1277
|
liveUpdatesDisconnected ? 'Live updates disconnected' :
|
|
1259
1278
|
showClusterWarmupLabel ? clusterLoadState.message :
|
|
1260
1279
|
crdDiscoveryStatus === 'discovering' ? 'Discovering Custom Resources…' :
|
|
@@ -1604,8 +1623,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
1604
1623
|
// Filter by namespace (client-side) and by visible kinds
|
|
1605
1624
|
const nsSet = namespaces.length > 0 ? new Set(namespaces) : null
|
|
1606
1625
|
const filteredNodes = displayedTopology.nodes.filter(node =>
|
|
1607
|
-
effectiveKinds.has(node.kind) &&
|
|
1608
|
-
(!nsSet || nsSet.has(node.data.namespace as string) || !(node.data.namespace as string))
|
|
1626
|
+
effectiveKinds.has(node.kind) && isInNamespaceScope(node, nsSet)
|
|
1609
1627
|
)
|
|
1610
1628
|
const filteredNodeIds = new Set(filteredNodes.map(n => n.id))
|
|
1611
1629
|
|
|
@@ -1630,6 +1648,16 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
1630
1648
|
}
|
|
1631
1649
|
}, [displayedTopology, visibleKinds, namespaces, topologyMode])
|
|
1632
1650
|
|
|
1651
|
+
// Namespace-scoped but NOT kind-filtered: the sidebar derives its kind list
|
|
1652
|
+
// and visible/hidden footer counts from this prop, so handing it the
|
|
1653
|
+
// kind-filtered graph nodes would drop every hidden kind from the list and
|
|
1654
|
+
// "hidden" would always read zero. Reads displayedTopology so the counts
|
|
1655
|
+
// freeze with the graph while paused.
|
|
1656
|
+
const filterSidebarNodes = useMemo(() => {
|
|
1657
|
+
if (!displayedTopology) return []
|
|
1658
|
+
return scopeNodesToNamespaces(displayedTopology.nodes, namespaces)
|
|
1659
|
+
}, [displayedTopology, namespaces])
|
|
1660
|
+
|
|
1633
1661
|
// Cluster Audit findings, joined onto topology nodes by the audit key the
|
|
1634
1662
|
// backend stamps on each node (data.auditKey). Only badge-worthy findings
|
|
1635
1663
|
// reach the graph; the raw auditDanger/auditWarning property names remain at
|
|
@@ -2021,7 +2049,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2021
2049
|
{/* Connecting view — shown during initial connection or retry.
|
|
2022
2050
|
Icon is pane-anchored so its screen position matches the
|
|
2023
2051
|
host hub splash across cross-document transitions. */}
|
|
2024
|
-
{!isSwitching && !(authMe?.authEnabled && !authMe?.username) && connection.state === 'connecting' && (
|
|
2052
|
+
{!isSwitching && !(authMe?.authEnabled && !authMe?.username) && connection.state === 'connecting' && !shellDuringSync && (
|
|
2025
2053
|
<PaneLoader
|
|
2026
2054
|
label="Connecting to cluster"
|
|
2027
2055
|
className="flex-1 min-h-0 bg-theme-base"
|
|
@@ -2082,13 +2110,21 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2082
2110
|
{/* inert while a fullscreen detail overlay covers the views — keeps the
|
|
2083
2111
|
retained background list out of the focus order + a11y tree (the visual
|
|
2084
2112
|
cover already blocks pointer events). */}
|
|
2085
|
-
{contentReady && <div className="flex-1 flex overflow-hidden" inert={expandedView}>
|
|
2113
|
+
{(contentReady || shellDuringSync) && <div className="flex-1 flex overflow-hidden" inert={expandedView}>
|
|
2086
2114
|
{/* Search included, not just the path: selection inside a view rides in
|
|
2087
2115
|
the query (?resource=, ?release=), so a path-only key would still
|
|
2088
2116
|
strand a crash on the view that produced it. */}
|
|
2089
2117
|
<ErrorBoundary resetKey={location.pathname + location.search}>
|
|
2118
|
+
{/* Initial sync in progress: views that need the full cluster dataset
|
|
2119
|
+
show per-kind progress instead; resource views work as kinds sync. */}
|
|
2120
|
+
{viewsSyncGated && connection.syncStatus && (
|
|
2121
|
+
<SyncProgressPanel
|
|
2122
|
+
syncStatus={connection.syncStatus}
|
|
2123
|
+
onNavigateToKind={(key) => navigate({ pathname: `/resources/${key}` })}
|
|
2124
|
+
/>
|
|
2125
|
+
)}
|
|
2090
2126
|
{/* Home dashboard */}
|
|
2091
|
-
{mainView === 'home' && (
|
|
2127
|
+
{!viewsSyncGated && mainView === 'home' && (
|
|
2092
2128
|
<HomeView
|
|
2093
2129
|
namespaces={namespaces}
|
|
2094
2130
|
topology={topology}
|
|
@@ -2147,7 +2183,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2147
2183
|
)}
|
|
2148
2184
|
|
|
2149
2185
|
{/* Topology view */}
|
|
2150
|
-
{mainView === 'topology' && (
|
|
2186
|
+
{!viewsSyncGated && mainView === 'topology' && (
|
|
2151
2187
|
<>
|
|
2152
2188
|
{topology?.requiresNamespaceFilter && namespaces.length === 0 ? (
|
|
2153
2189
|
/* Large cluster: prompt user to select a namespace */
|
|
@@ -2182,14 +2218,14 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2182
2218
|
<>
|
|
2183
2219
|
{/* Filter sidebar */}
|
|
2184
2220
|
<TopologyFilterSidebar
|
|
2185
|
-
nodes={
|
|
2221
|
+
nodes={filterSidebarNodes}
|
|
2186
2222
|
visibleKinds={visibleKinds}
|
|
2187
2223
|
onToggleKind={handleToggleKind}
|
|
2188
2224
|
onShowAll={handleShowAllKinds}
|
|
2189
2225
|
onHideAll={handleHideAllKinds}
|
|
2190
2226
|
collapsed={filterSidebarCollapsed}
|
|
2191
2227
|
onToggleCollapse={() => setFilterSidebarCollapsed(prev => !prev)}
|
|
2192
|
-
hiddenKinds={
|
|
2228
|
+
hiddenKinds={displayedTopology?.hiddenKinds}
|
|
2193
2229
|
onEnableHiddenKind={(kind) => {
|
|
2194
2230
|
setVisibleKinds(prev => new Set(prev).add(kind as NodeKind))
|
|
2195
2231
|
console.log(`[topology] User requested to show hidden kind: ${kind}`)
|
|
@@ -2275,7 +2311,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2275
2311
|
)}
|
|
2276
2312
|
|
|
2277
2313
|
{/* Timeline view */}
|
|
2278
|
-
{mainView === 'timeline' && (
|
|
2314
|
+
{!viewsSyncGated && mainView === 'timeline' && (
|
|
2279
2315
|
<TimelineView
|
|
2280
2316
|
namespaces={namespaces}
|
|
2281
2317
|
onResourceClick={(resource) => {
|
|
@@ -2293,7 +2329,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2293
2329
|
/>
|
|
2294
2330
|
)}
|
|
2295
2331
|
|
|
2296
|
-
{mainView === 'helm' && (
|
|
2332
|
+
{!viewsSyncGated && mainView === 'helm' && (
|
|
2297
2333
|
<HelmView
|
|
2298
2334
|
namespaces={namespaces}
|
|
2299
2335
|
selectedRelease={selectedHelmRelease}
|
|
@@ -2301,13 +2337,13 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2301
2337
|
/>
|
|
2302
2338
|
)}
|
|
2303
2339
|
|
|
2304
|
-
{mainView === 'helmCompare' && (
|
|
2340
|
+
{!viewsSyncGated && mainView === 'helmCompare' && (
|
|
2305
2341
|
<HelmCompareRoute />
|
|
2306
2342
|
)}
|
|
2307
2343
|
|
|
2308
2344
|
{/* GitOps view (inline only when the host hasn't taken it over — see
|
|
2309
2345
|
the takeover splash below). */}
|
|
2310
|
-
{mainView === 'gitops' && !isViewTakenOver('gitops') && (
|
|
2346
|
+
{!viewsSyncGated && mainView === 'gitops' && !isViewTakenOver('gitops') && (
|
|
2311
2347
|
<GitOpsView
|
|
2312
2348
|
namespaces={namespaces}
|
|
2313
2349
|
onOpenResource={(resource) => {
|
|
@@ -2317,12 +2353,14 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2317
2353
|
navigateToResource(resource)
|
|
2318
2354
|
}}
|
|
2319
2355
|
onClearNamespaces={clearAllNamespaces}
|
|
2320
|
-
|
|
2356
|
+
// Every GitOps "open Settings" ask is about the Argo CD connection
|
|
2357
|
+
// (diff CTA, per-resource health notice), so land on that section.
|
|
2358
|
+
onOpenSettings={() => openSettings('argocd')}
|
|
2321
2359
|
/>
|
|
2322
2360
|
)}
|
|
2323
2361
|
|
|
2324
2362
|
{/* Applications view — deployable software grouped by app/release evidence */}
|
|
2325
|
-
{mainView === 'applications' && (
|
|
2363
|
+
{!viewsSyncGated && mainView === 'applications' && (
|
|
2326
2364
|
<ApplicationsView
|
|
2327
2365
|
namespaces={namespaces}
|
|
2328
2366
|
onOpenResource={(resource) => {
|
|
@@ -2343,16 +2381,16 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2343
2381
|
)}
|
|
2344
2382
|
|
|
2345
2383
|
{/* Traffic view */}
|
|
2346
|
-
{mainView === 'traffic' && (
|
|
2384
|
+
{!viewsSyncGated && mainView === 'traffic' && (
|
|
2347
2385
|
<TrafficView namespaces={namespaces} />
|
|
2348
2386
|
)}
|
|
2349
2387
|
|
|
2350
2388
|
{/* Cost detail view */}
|
|
2351
|
-
{mainView === 'cost' && (
|
|
2389
|
+
{!viewsSyncGated && mainView === 'cost' && (
|
|
2352
2390
|
<CostView namespaces={namespaces} onBack={() => setMainView('home')} onOpenResource={navigateToResource} />
|
|
2353
2391
|
)}
|
|
2354
2392
|
|
|
2355
|
-
{mainView === 'capacity' && (
|
|
2393
|
+
{!viewsSyncGated && mainView === 'capacity' && (
|
|
2356
2394
|
<CapacityView onOpenResource={navigateToResource} />
|
|
2357
2395
|
)}
|
|
2358
2396
|
|
|
@@ -2370,7 +2408,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2370
2408
|
|
|
2371
2409
|
{/* Checks detail view. Cloud can take over fleet best practices while
|
|
2372
2410
|
the target-specific upgrade route continues to render locally. */}
|
|
2373
|
-
{mainView === 'checks' && (!isViewTakenOver('checks') || upgradeReadinessRoute) && (
|
|
2411
|
+
{!viewsSyncGated && mainView === 'checks' && (!isViewTakenOver('checks') || upgradeReadinessRoute) && (
|
|
2374
2412
|
<AuditView
|
|
2375
2413
|
namespaces={namespaces}
|
|
2376
2414
|
onNavigateToResource={navigateToResourceList}
|
|
@@ -2382,7 +2420,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2382
2420
|
Hub fleet uses; a GitOps reconciler subject routes to its detail page,
|
|
2383
2421
|
other resources open the standard resource view. Inline only when the
|
|
2384
2422
|
host hasn't taken it over. */}
|
|
2385
|
-
{mainView === 'issues' && !isViewTakenOver('issues') && (
|
|
2423
|
+
{!viewsSyncGated && mainView === 'issues' && !isViewTakenOver('issues') && (
|
|
2386
2424
|
<IssuesPane
|
|
2387
2425
|
namespaces={namespaces}
|
|
2388
2426
|
onNavigateToResource={navigateFromIssue}
|
|
@@ -2392,7 +2430,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2392
2430
|
{/* Workload full view — the standalone fullscreen route for non-list
|
|
2393
2431
|
surfaces and deep links. Expand-from-drawer is the ?full=1 overlay on
|
|
2394
2432
|
/resources instead, so it never routes here. */}
|
|
2395
|
-
{mainView === 'workload' && (
|
|
2433
|
+
{!viewsSyncGated && mainView === 'workload' && (
|
|
2396
2434
|
<WorkloadViewRoute
|
|
2397
2435
|
onNavigateToResource={(resource) => {
|
|
2398
2436
|
navigate(relatedResourcePath(resource))
|
|
@@ -2401,7 +2439,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2401
2439
|
)}
|
|
2402
2440
|
|
|
2403
2441
|
{/* Compare two resources of the same kind side-by-side */}
|
|
2404
|
-
{mainView === 'compare' && <CompareViewRoute />}
|
|
2442
|
+
{!viewsSyncGated && mainView === 'compare' && <CompareViewRoute />}
|
|
2405
2443
|
|
|
2406
2444
|
</ErrorBoundary>
|
|
2407
2445
|
</div>}
|
|
@@ -2410,7 +2448,7 @@ function AppInner({ manageDocumentTitle = false, documentTitleSuffix, onClusterL
|
|
|
2410
2448
|
{/* Resource detail drawer — stays mounted, expands to full-screen WorkloadView.
|
|
2411
2449
|
Gated on contentReady so it never renders over the connecting/switching
|
|
2412
2450
|
splash (which would push the centered logo off-center). */}
|
|
2413
|
-
{contentReady && resourceDrawer.shouldRender && drawerResource && (
|
|
2451
|
+
{(contentReady || shellDuringSync) && resourceDrawer.shouldRender && drawerResource && (
|
|
2414
2452
|
<ResourceDetailDrawer
|
|
2415
2453
|
resource={drawerResource}
|
|
2416
2454
|
initialTab={drawerInitialTab}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
1
|
+
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
// Deterministic config so the 401 handler's basename/routePath math is fixed and
|
|
4
4
|
// independent of window/env. client.ts only consumes these six exports.
|
|
@@ -67,6 +67,17 @@ function make401(authMode: string): Response {
|
|
|
67
67
|
} as unknown as Response
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// Each test re-imports ./client after resetModules, and the first import in the
|
|
71
|
+
// file is the one that transforms client.ts and everything it pulls in. That is
|
|
72
|
+
// seconds of work on a contended machine, and it would otherwise be charged to
|
|
73
|
+
// whichever test happens to run first, failing it on timeout while the rest of
|
|
74
|
+
// the suite passes in under a second. Pay it once, outside any test's budget —
|
|
75
|
+
// resetModules drops module instances but keeps the transform cache, so the
|
|
76
|
+
// per-test re-import stays cheap.
|
|
77
|
+
beforeAll(async () => {
|
|
78
|
+
await import('./client')
|
|
79
|
+
}, 120_000)
|
|
80
|
+
|
|
70
81
|
beforeEach(() => {
|
|
71
82
|
// Reset module state so the redirect gate starts fresh.
|
|
72
83
|
vi.resetModules()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
PROM_STATUS_POLL_DISCOVERING_MS,
|
|
4
|
+
PROM_STATUS_POLL_IDLE_MS,
|
|
5
|
+
prometheusStatusRefetchInterval,
|
|
6
|
+
} from './client'
|
|
7
|
+
|
|
8
|
+
describe('prometheusStatusRefetchInterval', () => {
|
|
9
|
+
it('polls quickly while discovery is in flight or has not reported yet', () => {
|
|
10
|
+
expect(prometheusStatusRefetchInterval(undefined)).toBe(PROM_STATUS_POLL_IDLE_MS)
|
|
11
|
+
// Read before the server's run started: transient, keep polling.
|
|
12
|
+
expect(prometheusStatusRefetchInterval({ available: false, connected: false })).toBe(PROM_STATUS_POLL_DISCOVERING_MS)
|
|
13
|
+
// A run that ended without a connection carries its outcome.
|
|
14
|
+
expect(prometheusStatusRefetchInterval({ available: false, connected: false, error: 'no Prometheus service found in cluster' })).toBe(
|
|
15
|
+
PROM_STATUS_POLL_IDLE_MS,
|
|
16
|
+
)
|
|
17
|
+
expect(prometheusStatusRefetchInterval({ available: false, connected: false, discovering: true })).toBe(
|
|
18
|
+
PROM_STATUS_POLL_DISCOVERING_MS,
|
|
19
|
+
)
|
|
20
|
+
expect(prometheusStatusRefetchInterval({ available: true, connected: true, address: 'http://localhost:1' })).toBe(
|
|
21
|
+
PROM_STATUS_POLL_IDLE_MS,
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
})
|