@skyhook-io/k8s-ui 1.16.3 → 1.17.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.
Files changed (309) hide show
  1. package/README.md +41 -0
  2. package/package.json +3 -3
  3. package/src/components/applications/ApplicationDetail.test.tsx +15 -0
  4. package/src/components/applications/ApplicationDetail.tsx +17 -15
  5. package/src/components/charts/AreaChart.interaction.test.tsx +118 -0
  6. package/src/components/charts/AreaChart.test.tsx +25 -0
  7. package/src/components/charts/AreaChart.tsx +141 -12
  8. package/src/components/charts/index.ts +1 -0
  9. package/src/components/checks/ChecksView.tsx +18 -1
  10. package/src/components/checks/types.ts +2 -13
  11. package/src/components/cnpg/CNPGBackupSummary.tsx +57 -9
  12. package/src/components/cnpg/CNPGClusterHASection.test.tsx +90 -0
  13. package/src/components/cnpg/CNPGClusterHASection.tsx +331 -0
  14. package/src/components/cnpg/CNPGClusterSummary.test.tsx +275 -0
  15. package/src/components/cnpg/CNPGClusterSummary.tsx +310 -96
  16. package/src/components/cnpg/CNPGConnectSection.test.tsx +64 -0
  17. package/src/components/cnpg/CNPGConnectSection.tsx +178 -0
  18. package/src/components/cnpg/CNPGDeclarativeSummary.tsx +157 -26
  19. package/src/components/cnpg/CNPGImageCatalogSummary.tsx +7 -4
  20. package/src/components/cnpg/CNPGLogicalPath.test.tsx +22 -0
  21. package/src/components/cnpg/CNPGLogicalPath.tsx +150 -0
  22. package/src/components/cnpg/CNPGObjectStoreSummary.tsx +9 -6
  23. package/src/components/cnpg/CNPGObjectSummary.test.tsx +193 -2
  24. package/src/components/cnpg/CNPGPoolerScheduling.test.tsx +36 -0
  25. package/src/components/cnpg/CNPGPoolerSummary.tsx +241 -14
  26. package/src/components/cnpg/CNPGSharedSummary.tsx +15 -8
  27. package/src/components/cnpg/CNPGWALArchivingFact.test.tsx +39 -0
  28. package/src/components/cnpg/backupRuns.test.ts +17 -0
  29. package/src/components/cnpg/backupRuns.ts +19 -0
  30. package/src/components/cnpg/connect.test.ts +124 -0
  31. package/src/components/cnpg/connect.ts +155 -0
  32. package/src/components/cnpg/databaseRole.test.ts +38 -0
  33. package/src/components/cnpg/databaseRole.ts +80 -0
  34. package/src/components/cnpg/ha.test.ts +446 -0
  35. package/src/components/cnpg/ha.ts +621 -0
  36. package/src/components/cnpg/index.ts +18 -0
  37. package/src/components/cnpg/logicalReplication.test.ts +121 -0
  38. package/src/components/cnpg/logicalReplication.ts +252 -0
  39. package/src/components/cnpg/pooler.test.ts +84 -0
  40. package/src/components/cnpg/pooler.ts +225 -0
  41. package/src/components/cnpg/primitives.tsx +21 -117
  42. package/src/components/cnpg/problems.ts +12 -0
  43. package/src/components/cnpg/relations.test.ts +63 -13
  44. package/src/components/cnpg/relations.ts +61 -52
  45. package/src/components/cnpg/schedule.ts +35 -0
  46. package/src/components/cnpg/workspace-disk.test.ts +97 -0
  47. package/src/components/cnpg/workspace-fleet-metrics.test.ts +250 -0
  48. package/src/components/cnpg/workspace-problems.test.ts +175 -0
  49. package/src/components/cnpg/workspace.test.ts +380 -9
  50. package/src/components/cnpg/workspace.ts +1137 -136
  51. package/src/components/dock/DockContext.test.tsx +32 -0
  52. package/src/components/dock/DockContext.tsx +18 -4
  53. package/src/components/dock/LocalTerminalTab.test.tsx +40 -0
  54. package/src/components/dock/LocalTerminalTab.tsx +4 -3
  55. package/src/components/dock/TerminalTab.tsx +20 -8
  56. package/src/components/facts/certainty.tsx +36 -0
  57. package/src/components/facts/facts.test.tsx +24 -0
  58. package/src/components/facts/facts.tsx +62 -0
  59. package/src/components/facts/index.ts +6 -0
  60. package/src/components/facts/managed-by.test.tsx +17 -0
  61. package/src/components/facts/managed-by.tsx +36 -0
  62. package/src/components/facts/public-exports.test.tsx +35 -0
  63. package/src/components/gitops/GitOpsGraphFilterRail.tsx +6 -2
  64. package/src/components/gitops/GitOpsHealthSourceNotice.test.tsx +38 -0
  65. package/src/components/gitops/GitOpsHealthSourceNotice.tsx +13 -3
  66. package/src/components/gitops/GitOpsTableView.tsx +9 -24
  67. package/src/components/gitops/detail-helpers.ts +1 -1
  68. package/src/components/gitops/insights/ArgoResourceDiff.test.tsx +115 -0
  69. package/src/components/gitops/insights/ArgoResourceDiff.tsx +22 -5
  70. package/src/components/gitops/insights/DriftPanel.tsx +62 -0
  71. package/src/components/gitops/insights/GitOpsInsightViews.tsx +29 -83
  72. package/src/components/gitops/tree/GitOpsTreeGraph.test.ts +26 -1
  73. package/src/components/gitops/tree/GitOpsTreeGraph.tsx +40 -60
  74. package/src/components/gitops/tree/layout.test.ts +111 -0
  75. package/src/components/gitops/tree/layout.ts +128 -0
  76. package/src/components/gitops/tree/tree-helpers.test.ts +17 -0
  77. package/src/components/gitops/tree/tree-helpers.ts +7 -1
  78. package/src/components/issues/ConditionalCleanup.test.tsx +82 -0
  79. package/src/components/issues/IssuesView.tsx +40 -5
  80. package/src/components/issues/ResourceIssuesSection.test.tsx +28 -0
  81. package/src/components/issues/ResourceIssuesSection.tsx +3 -0
  82. package/src/components/issues/index.ts +3 -1
  83. package/src/components/issues/issue-timing.ts +14 -3
  84. package/src/components/issues/issues.test.ts +50 -2
  85. package/src/components/issues/severity.ts +21 -0
  86. package/src/components/issues/types.ts +23 -0
  87. package/src/components/logs/LogCore.theme.test.tsx +81 -3
  88. package/src/components/logs/LogCore.tsx +25 -17
  89. package/src/components/logs/LogToolbarSelects.tsx +3 -1
  90. package/src/components/logs/LogsViewer.test.tsx +291 -0
  91. package/src/components/logs/LogsViewer.tsx +72 -11
  92. package/src/components/logs/StructuredLogLine.test.tsx +76 -0
  93. package/src/components/logs/StructuredLogLine.tsx +22 -30
  94. package/src/components/logs/WorkloadLogsViewer.notice.test.tsx +59 -0
  95. package/src/components/logs/WorkloadLogsViewer.test.tsx +65 -0
  96. package/src/components/logs/WorkloadLogsViewer.tsx +44 -10
  97. package/src/components/logs/useLogBuffer.test.ts +8 -0
  98. package/src/components/logs/useLogStream.ts +28 -1
  99. package/src/components/problems/index.ts +1 -0
  100. package/src/components/problems/problems.test.tsx +63 -0
  101. package/src/components/problems/problems.tsx +165 -0
  102. package/src/components/resources/ActiveFilterBar.test.tsx +78 -0
  103. package/src/components/resources/ActiveFilterBar.tsx +86 -0
  104. package/src/components/resources/ResourcesSidebar.keyboard.test.tsx +42 -0
  105. package/src/components/resources/ResourcesSidebar.test.tsx +36 -2
  106. package/src/components/resources/ResourcesSidebar.tsx +92 -41
  107. package/src/components/resources/ResourcesView.kind-filter-memory.test.ts +52 -0
  108. package/src/components/resources/ResourcesView.kind-view.test.tsx +149 -0
  109. package/src/components/resources/ResourcesView.per-container.test.ts +56 -1
  110. package/src/components/resources/ResourcesView.tsx +309 -117
  111. package/src/components/resources/curated-column-ownership.test.ts +3 -3
  112. package/src/components/resources/renderers/AnalysisRunRenderer.tsx +3 -3
  113. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +6 -5
  114. package/src/components/resources/renderers/AutoscalingReferences.test.tsx +286 -0
  115. package/src/components/resources/renderers/CAPIClusterRenderer.tsx +5 -4
  116. package/src/components/resources/renderers/CAPIExactReferenceNavigation.test.tsx +27 -0
  117. package/src/components/resources/renderers/CAPIKubeadmControlPlaneRenderer.tsx +2 -2
  118. package/src/components/resources/renderers/CAPIMachineDeploymentRenderer.tsx +2 -2
  119. package/src/components/resources/renderers/CAPIMachinePoolRenderer.tsx +2 -2
  120. package/src/components/resources/renderers/CAPIMachineRenderer.tsx +2 -2
  121. package/src/components/resources/renderers/CAPIMachineSetRenderer.tsx +2 -2
  122. package/src/components/resources/renderers/CNPGBackupRenderer.test.tsx +23 -0
  123. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +8 -19
  124. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +7 -8
  125. package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +25 -0
  126. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.test.tsx +32 -0
  127. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +9 -17
  128. package/src/components/resources/renderers/CNPGShared.tsx +2 -2
  129. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -2
  130. package/src/components/resources/renderers/CompositeRenderer.navigation.test.tsx +35 -0
  131. package/src/components/resources/renderers/CompositeRenderer.tsx +3 -3
  132. package/src/components/resources/renderers/CompositionRenderer.tsx +3 -3
  133. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -2
  134. package/src/components/resources/renderers/ContourExactReferenceNavigation.test.tsx +34 -0
  135. package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +11 -10
  136. package/src/components/resources/renderers/CronJobRenderer.tsx +6 -4
  137. package/src/components/resources/renderers/CronWorkflowRenderer.tsx +3 -2
  138. package/src/components/resources/renderers/EndpointSliceRenderer.test.tsx +62 -0
  139. package/src/components/resources/renderers/EndpointSliceRenderer.tsx +15 -14
  140. package/src/components/resources/renderers/EventRenderer.tsx +3 -3
  141. package/src/components/resources/renderers/ExperimentRenderer.tsx +2 -2
  142. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -2
  143. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +2 -2
  144. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +4 -4
  145. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +11 -24
  146. package/src/components/resources/renderers/GatewayRouteReferences.test.tsx +63 -0
  147. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +3 -3
  148. package/src/components/resources/renderers/HPARenderer.tsx +57 -17
  149. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +12 -25
  150. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +3 -3
  151. package/src/components/resources/renderers/IngressClassReferences.test.tsx +52 -0
  152. package/src/components/resources/renderers/IngressClassRenderer.tsx +10 -3
  153. package/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx +33 -0
  154. package/src/components/resources/renderers/IngressRenderer.tsx +36 -5
  155. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +9 -9
  156. package/src/components/resources/renderers/IstioExactReferenceNavigation.test.tsx +34 -0
  157. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -8
  158. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -14
  159. package/src/components/resources/renderers/KedaCredentials.test.tsx +85 -0
  160. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +15 -13
  161. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +6 -3
  162. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +24 -31
  163. package/src/components/resources/renderers/KnativeExactReferenceNavigation.test.tsx +47 -0
  164. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +6 -7
  165. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +7 -9
  166. package/src/components/resources/renderers/KnativeReferenceLink.tsx +14 -0
  167. package/src/components/resources/renderers/KnativeReferenceLinks.test.tsx +60 -0
  168. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +9 -11
  169. package/src/components/resources/renderers/KustomizationRenderer.tsx +2 -2
  170. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +3 -2
  171. package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +4 -3
  172. package/src/components/resources/renderers/LeaseRenderer.tsx +3 -15
  173. package/src/components/resources/renderers/ManagedResourceRenderer.tsx +2 -2
  174. package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +3 -2
  175. package/src/components/resources/renderers/NamespaceRenderer.test.ts +69 -2
  176. package/src/components/resources/renderers/NamespaceRenderer.tsx +6 -1
  177. package/src/components/resources/renderers/NodeRenderer.test.tsx +81 -1
  178. package/src/components/resources/renderers/NodeRenderer.tsx +56 -15
  179. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +3 -3
  180. package/src/components/resources/renderers/PodClassReferences.test.tsx +42 -0
  181. package/src/components/resources/renderers/PodNomination.test.tsx +57 -0
  182. package/src/components/resources/renderers/PodRenderer.test.tsx +32 -0
  183. package/src/components/resources/renderers/PodRenderer.tsx +62 -36
  184. package/src/components/resources/renderers/RolloutRenderer.test.tsx +1 -1
  185. package/src/components/resources/renderers/RolloutRenderer.tsx +3 -2
  186. package/src/components/resources/renderers/SbomReportRenderer.tsx +2 -2
  187. package/src/components/resources/renderers/SecretRenderer.test.tsx +88 -0
  188. package/src/components/resources/renderers/SecretRenderer.tsx +34 -5
  189. package/src/components/resources/renderers/ServiceRenderer.test.tsx +43 -0
  190. package/src/components/resources/renderers/ServiceRenderer.tsx +11 -3
  191. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +9 -22
  192. package/src/components/resources/renderers/VPARenderer.tsx +17 -11
  193. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +5 -7
  194. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +3 -3
  195. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +2 -2
  196. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +3 -3
  197. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +2 -2
  198. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +2 -2
  199. package/src/components/resources/renderers/WorkflowRenderer.tsx +4 -3
  200. package/src/components/resources/renderers/kyverno-modern-cells.tsx +1 -1
  201. package/src/components/resources/renderers/rollout/ReplicaSetProgression.tsx +2 -2
  202. package/src/components/resources/resource-utils-capi.ts +1 -1
  203. package/src/components/resources/resource-utils-cnpg.test.ts +35 -10
  204. package/src/components/resources/resource-utils-cnpg.ts +51 -24
  205. package/src/components/resources/resource-utils-contour.test.ts +44 -0
  206. package/src/components/resources/resource-utils-contour.ts +16 -0
  207. package/src/components/resources/resource-utils-istio.ts +12 -8
  208. package/src/components/resources/resource-utils-keda.ts +1 -7
  209. package/src/components/resources/resource-utils-knative.ts +18 -0
  210. package/src/components/resources/resource-utils-kyverno.ts +1 -7
  211. package/src/components/resources/resource-utils-velero.ts +0 -12
  212. package/src/components/resources/resource-utils.ts +99 -39
  213. package/src/components/resources/rules-column-width.test.ts +1 -1
  214. package/src/components/resources/summarize-scheduler-message.test.ts +30 -0
  215. package/src/components/shared/ActionConfirmDialog.test.tsx +154 -0
  216. package/src/components/shared/ActionConfirmDialog.tsx +246 -0
  217. package/src/components/shared/CreateResourceDialog.test.tsx +330 -0
  218. package/src/components/shared/CreateResourceDialog.tsx +126 -80
  219. package/src/components/shared/DetailShell.test.tsx +80 -0
  220. package/src/components/shared/DetailShell.tsx +85 -10
  221. package/src/components/shared/DrainPlanDialog.tsx +1 -0
  222. package/src/components/shared/EditableYamlView.test.tsx +7 -0
  223. package/src/components/shared/EditableYamlView.tsx +1 -1
  224. package/src/components/shared/GitOpsWriteWarning.test.tsx +86 -0
  225. package/src/components/shared/GitOpsWriteWarning.tsx +135 -0
  226. package/src/components/shared/ResourceActionsBar.tsx +20 -24
  227. package/src/components/shared/ResourceRendererDispatch.tsx +13 -10
  228. package/src/components/shared/SetImageDialog.test.ts +75 -6
  229. package/src/components/shared/SetImageDialog.tsx +92 -84
  230. package/src/components/shared/index.ts +4 -1
  231. package/src/components/timeline/TimelineList.tsx +2 -15
  232. package/src/components/timeline/TimelineSwimlanes.tsx +3 -11
  233. package/src/components/timeline/timeline-filters.ts +1 -0
  234. package/src/components/topology/K8sResourceNode.test.ts +18 -1
  235. package/src/components/topology/K8sResourceNode.tsx +44 -2
  236. package/src/components/topology/TopologyFilterSidebar.tsx +1 -0
  237. package/src/components/topology/TopologyGraph.tsx +8 -74
  238. package/src/components/topology/TopologySearch.tsx +1 -0
  239. package/src/components/topology/internet-groups.test.ts +35 -0
  240. package/src/components/topology/internet-groups.ts +55 -0
  241. package/src/components/topology/layout.ts +1 -1
  242. package/src/components/ui/Badge.tsx +1 -0
  243. package/src/components/ui/FoldSection.tsx +68 -0
  244. package/src/components/ui/ForceDeleteConfirmDialog.test.tsx +62 -0
  245. package/src/components/ui/ForceDeleteConfirmDialog.tsx +24 -12
  246. package/src/components/ui/RefLink.tsx +20 -0
  247. package/src/components/ui/RowActionMenu.tsx +8 -3
  248. package/src/components/ui/ScheduleValue.test.tsx +5 -0
  249. package/src/components/ui/ScheduleValue.tsx +3 -2
  250. package/src/components/ui/crd-definition-navigation.test.tsx +160 -0
  251. package/src/components/ui/drawer-components.test.tsx +175 -1
  252. package/src/components/ui/drawer-components.tsx +103 -36
  253. package/src/components/ui/index.ts +3 -1
  254. package/src/components/ui/monacoEditorFeatures.ts +77 -0
  255. package/src/components/ui/monacoRuntime.ts +14 -19
  256. package/src/components/ui/status-tone.test.ts +10 -1
  257. package/src/components/ui/status-tone.tsx +29 -0
  258. package/src/components/workload/WorkloadView.deleted.test.tsx +101 -0
  259. package/src/components/workload/WorkloadView.deletedDialog.test.tsx +45 -0
  260. package/src/components/workload/WorkloadView.relationships.test.tsx +37 -0
  261. package/src/components/workload/WorkloadView.secretSave.test.tsx +62 -0
  262. package/src/components/workload/WorkloadView.spec.test.tsx +149 -0
  263. package/src/components/workload/WorkloadView.tsx +297 -104
  264. package/src/index.ts +14 -1
  265. package/src/monaco-deep.d.ts +19 -12
  266. package/src/theme/components.css +13 -0
  267. package/src/types/capacity.ts +21 -2
  268. package/src/types/core.ts +60 -2
  269. package/src/types/gitops-flux-status.test.ts +32 -1
  270. package/src/types/gitops-insights.ts +19 -2
  271. package/src/types/gitops-tree.ts +10 -0
  272. package/src/types/gitops.ts +26 -6
  273. package/src/utils/api-resources.ts +7 -0
  274. package/src/utils/application-history.ts +1 -0
  275. package/src/utils/cnpg-backup.test.ts +26 -0
  276. package/src/utils/cnpg-backup.ts +70 -0
  277. package/src/utils/format.test.ts +49 -2
  278. package/src/utils/format.ts +9 -1
  279. package/src/utils/gateway-references.test.ts +17 -0
  280. package/src/utils/gateway-references.ts +29 -0
  281. package/src/utils/gitops-owner.ts +3 -2
  282. package/src/utils/gitops-write-guard.test.ts +401 -0
  283. package/src/utils/gitops-write-guard.ts +509 -0
  284. package/src/utils/grant.test.ts +19 -0
  285. package/src/utils/grant.ts +35 -0
  286. package/src/utils/index.ts +6 -0
  287. package/src/utils/ingress-class-references.ts +22 -0
  288. package/src/utils/k8s-errors.ts +7 -7
  289. package/src/utils/log-format.test.ts +41 -0
  290. package/src/utils/log-format.ts +71 -15
  291. package/src/utils/log-level.ts +12 -8
  292. package/src/utils/navigation.test.ts +70 -0
  293. package/src/utils/navigation.ts +91 -8
  294. package/src/utils/node-lifecycle.test.ts +36 -0
  295. package/src/utils/node-lifecycle.ts +77 -0
  296. package/src/utils/object-reference-navigation.test.ts +43 -0
  297. package/src/utils/resource-hierarchy.test.ts +69 -0
  298. package/src/utils/resource-hierarchy.ts +41 -7
  299. package/src/utils/resource-icons.ts +2 -0
  300. package/src/utils/resource-refs.test.ts +23 -0
  301. package/src/utils/resource-refs.ts +30 -0
  302. package/src/utils/skeleton-yaml.test.ts +21 -0
  303. package/src/utils/skeleton-yaml.ts +16 -0
  304. package/src/utils/url-path.test.ts +35 -0
  305. package/src/utils/url-path.ts +21 -0
  306. package/src/utils/validators.test.ts +4 -13
  307. package/src/utils/validators.ts +5 -19
  308. package/src/utils/view-transition.test.ts +35 -1
  309. package/src/utils/view-transition.ts +16 -1
@@ -1,12 +1,20 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import { renderToString } from 'react-dom/server'
3
- import { EventsSection, ProblemAlerts, OperationalIssuesShownContext, RelatedResourcesSection } from './drawer-components'
3
+ import { AlertBanner, EventsSection, ProblemAlerts, OperationalIssuesShownContext, RelatedResourcesSection, defaultConditionTone } from './drawer-components'
4
4
 
5
5
  const problems = [
6
6
  { color: 'red' as const, message: 'Application is Degraded' },
7
7
  { color: 'yellow' as const, message: 'Application is OutOfSync' },
8
8
  ]
9
9
 
10
+ describe('GKE node condition polarity', () => {
11
+ it.each(['StoragePressureRootFileSystem', 'DPv2MigrationUnsupportedCNI', 'UnsupportedEBPFPrograms'])('%s is a problem only when True', type => {
12
+ expect(defaultConditionTone({ type, status: 'False' })).toBe('ok')
13
+ expect(defaultConditionTone({ type, status: 'True' })).toBe('fail')
14
+ expect(defaultConditionTone({ type, status: 'Unknown' })).toBe('unknown')
15
+ })
16
+ })
17
+
10
18
  describe('ProblemAlerts', () => {
11
19
  it('renders every problem', () => {
12
20
  const html = renderToString(<ProblemAlerts problems={problems} />)
@@ -62,3 +70,169 @@ describe('Recent Events layout', () => {
62
70
  expect(fullscreen).toContain('Reason23')
63
71
  })
64
72
  })
73
+
74
+ describe('dependency relationships', () => {
75
+ it('renders a dependency-only response with the existing navigation control', () => {
76
+ const html = renderToString(<RelatedResourcesSection relationships={{ dependencies: [{ kind: 'Issuer', group: 'cert-manager.io', namespace: 'team', name: 'ca' }] }} onNavigate={() => {}} />)
77
+ expect(html).toContain('Depends On')
78
+ expect(html).toContain('ca')
79
+ expect(html).toContain('<button')
80
+ expect(html).not.toContain('Scale Target')
81
+ })
82
+
83
+ it('renders the reverse dependency without calling it an autoscaler', () => {
84
+ const html = renderToString(<RelatedResourcesSection relationships={{ dependents: [{ kind: 'Certificate', group: 'cert-manager.io', namespace: 'team', name: 'tls' }] }} />)
85
+ expect(html).toContain('Required By')
86
+ expect(html).toContain('tls')
87
+ expect(html).not.toContain('Autoscaler')
88
+ })
89
+ })
90
+
91
+ describe('versioned dependency projections', () => {
92
+ const issuer = { kind: 'Issuer', group: 'cert-manager.io', namespace: 'team', name: 'ca' }
93
+ const certificate = { kind: 'Certificate', group: 'cert-manager.io', namespace: 'team', name: 'tls' }
94
+
95
+ it('presents mirrored configuration dependencies once under the specific labels', () => {
96
+ const html = renderToString(<RelatedResourcesSection relationships={{ dependencies: [issuer], configRefs: [issuer], dependents: [certificate], consumers: [certificate] }} onNavigate={() => {}} />)
97
+ expect(html).toContain('Depends On')
98
+ expect(html).toContain('Required By')
99
+ expect(html).not.toContain('Configuration')
100
+ expect(html).not.toContain('Used By')
101
+ expect(html.match(/<button/g)).toHaveLength(3)
102
+ })
103
+
104
+ it('preserves configuration and consumer groups when dependency fields are absent', () => {
105
+ const html = renderToString(<RelatedResourcesSection relationships={{ configRefs: [issuer], consumers: [certificate] }} />)
106
+ expect(html).toContain('Configuration')
107
+ expect(html).toContain('Used By')
108
+ expect(html).not.toContain('Depends On')
109
+ })
110
+
111
+ it('retains navigation controls for identically named resources from distinct API groups', () => {
112
+ const html = renderToString(<RelatedResourcesSection relationships={{ dependencies: [issuer, { ...issuer, group: 'other.example.com' }, issuer] }} onNavigate={() => {}} />)
113
+ expect(html.match(/<button/g)).toHaveLength(3)
114
+ })
115
+ })
116
+
117
+ describe('namespace deletion condition polarity', () => {
118
+ it.each([
119
+ 'NamespaceDeletionDiscoveryFailure',
120
+ 'NamespaceDeletionGroupVersionParsingFailure',
121
+ 'NamespaceDeletionContentFailure',
122
+ 'NamespaceContentRemaining',
123
+ 'NamespaceFinalizersRemaining',
124
+ ])('%s treats True as a blocker and False as cleared', (type) => {
125
+ expect(defaultConditionTone({ type, status: 'True' })).toBe('fail')
126
+ expect(defaultConditionTone({ type, status: 'False' })).toBe('ok')
127
+ expect(defaultConditionTone({ type, status: 'Unknown' })).toBe('unknown')
128
+ })
129
+ })
130
+
131
+ describe('monitors projection', () => {
132
+ it('renders Monitored By alone and deduplicates by API group', () => {
133
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
134
+ const html = renderToString(<RelatedResourcesSection relationships={{ monitors: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
135
+ expect(html).toContain('Related Resources')
136
+ expect(html).toContain('Monitored By')
137
+ expect(html.match(/<button/g)).toHaveLength(3)
138
+ })
139
+ })
140
+
141
+
142
+ describe('monitorTargets projection', () => {
143
+ it('renders Monitors alone and deduplicates by API group', () => {
144
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
145
+ const html = renderToString(<RelatedResourcesSection relationships={{ monitorTargets: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
146
+ expect(html).toContain('Related Resources')
147
+ expect(html).toContain('Monitors')
148
+ expect(html.match(/<button/g)).toHaveLength(3)
149
+ })
150
+ })
151
+
152
+
153
+ describe('backends projection', () => {
154
+ it('renders Routes To alone and deduplicates by API group', () => {
155
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
156
+ const html = renderToString(<RelatedResourcesSection relationships={{ backends: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
157
+ expect(html).toContain('Related Resources')
158
+ expect(html).toContain('Routes To')
159
+ expect(html.match(/<button/g)).toHaveLength(3)
160
+ })
161
+ })
162
+
163
+
164
+ describe('routedFrom projection', () => {
165
+ it('renders Routed From alone and deduplicates by API group', () => {
166
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
167
+ const html = renderToString(<RelatedResourcesSection relationships={{ routedFrom: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
168
+ expect(html).toContain('Related Resources')
169
+ expect(html).toContain('Routed From')
170
+ expect(html.match(/<button/g)).toHaveLength(3)
171
+ })
172
+ })
173
+
174
+
175
+ describe('protects projection', () => {
176
+ it('renders Selects alone and deduplicates by API group', () => {
177
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
178
+ const html = renderToString(<RelatedResourcesSection relationships={{ protects: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
179
+ expect(html).toContain('Related Resources')
180
+ expect(html).toContain('Selects')
181
+ expect(html.match(/<button/g)).toHaveLength(3)
182
+ })
183
+ })
184
+
185
+
186
+ describe('healthChecks projection', () => {
187
+ it('renders Health Checks alone and deduplicates by API group', () => {
188
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
189
+ const html = renderToString(<RelatedResourcesSection relationships={{ healthChecks: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
190
+ expect(html).toContain('Related Resources')
191
+ expect(html).toContain('Health Checks')
192
+ expect(html.match(/<button/g)).toHaveLength(3)
193
+ })
194
+ })
195
+
196
+
197
+ describe('stagedPolicies projection', () => {
198
+ it('renders Staged Policies alone and deduplicates by API group', () => {
199
+ const ref = { kind: 'Widget', group: 'one.example', namespace: 'demo', name: 'shared' }
200
+ const html = renderToString(<RelatedResourcesSection relationships={{ stagedPolicies: [ref, ref, { ...ref, group: 'two.example' }] }} onNavigate={() => {}} />)
201
+ expect(html).toContain('Related Resources')
202
+ expect(html).toContain('Staged Policies')
203
+ expect(html.match(/<button/g)).toHaveLength(3)
204
+ })
205
+ })
206
+
207
+
208
+ describe('routing version skew', () => {
209
+ it('keeps older Services, Gateways, Routes and Pods fields visible', () => {
210
+ const html = renderToString(<RelatedResourcesSection relationships={{ services: [{kind: 'Service', namespace: 'demo', name: 'web'}], gateways: [{kind: 'HTTPRoute', group: 'gateway.networking.k8s.io', namespace: 'demo', name: 'old-route'}], routes: [{kind: 'IngressRoute', group: 'traefik.io', namespace: 'demo', name: 'route'}], pods: [{kind: 'Deployment', group: 'apps', namespace: 'demo', name: 'old-target'}] }} />)
211
+ for (const label of ['Services', 'Gateways', 'Routes', 'Pods', 'old-route', 'old-target']) expect(html).toContain(label)
212
+ })
213
+ })
214
+
215
+ describe('related resource chips', () => {
216
+ it('tells a Knative Service apart from a core Service', () => {
217
+ const html = renderToString(<RelatedResourcesSection relationships={{
218
+ services: [{ kind: 'Service', namespace: 'team', name: 'web-private' }],
219
+ routedFrom: [{ kind: 'Service', group: 'serving.knative.dev', namespace: 'team', name: 'web' }],
220
+ }} onNavigate={() => {}} />)
221
+ expect(html).toContain('>svc<!-- -->/</span>web-private<')
222
+ expect(html).toContain('>ksvc<!-- -->/</span>web<')
223
+ })
224
+ })
225
+
226
+
227
+ describe('AlertBanner optional actions', () => {
228
+ it('retains the title-only default without introducing a layout wrapper', () => {
229
+ const html = renderToString(<AlertBanner variant="info" title="Paused" />)
230
+ expect(html).toMatch(/<div class="text-sm font-medium [^"]*">Paused<\/div>/)
231
+ expect(html).not.toContain('justify-between'); expect(html).not.toContain('flex-1')
232
+ })
233
+ it.each([undefined, 'Check connection'])('places the optional action next to the title, retaining the body (%s)', message => {
234
+ const html = renderToString(<AlertBanner variant="warning" title="Metrics unavailable" message={message} action={<button>Open settings</button>} />)
235
+ expect(html).toContain('Metrics unavailable'); expect(html).toContain('<button>Open settings</button>')
236
+ expect(html).toContain('flex-wrap'); if (message) expect(html).toContain(message)
237
+ })
238
+ })
@@ -1,8 +1,10 @@
1
+ import { dedupeResourceRefs, omitResourceRefs } from '../../utils/resource-refs'
2
+ import { customResourceDefinitionRef, getCRDDefinitionsSnapshot, subscribeCRDDefinitions } from '../../utils/navigation'
1
3
  import { Disclosure } from './Disclosure'
2
- import { createContext, useContext, useState } from 'react'
4
+ import { createContext, useContext, useState, useSyncExternalStore } from 'react'
3
5
  import { Copy, Check, Tag, AlertTriangle, CheckCircle, ExternalLink, Layers, X, Minus } from 'lucide-react'
4
6
  import { clsx } from 'clsx'
5
- import { formatAge, formatDuration, formatResources } from '../resources/resource-utils'
7
+ import { formatDuration, formatResources } from '../resources/resource-utils'
6
8
  import { getEffectiveResources } from '../../utils/extended-resources'
7
9
  import { Tooltip } from './Tooltip'
8
10
  import { getKindColorClass } from '../ui/Badge'
@@ -206,6 +208,12 @@ const NEGATIVE_POLARITY_TYPES = new Set([
206
208
  'NetworkUnavailable',
207
209
  // Core K8s — Pod
208
210
  'DisruptionTarget',
211
+ // Core K8s — Namespace
212
+ 'NamespaceDeletionDiscoveryFailure',
213
+ 'NamespaceDeletionGroupVersionParsingFailure',
214
+ 'NamespaceDeletionContentFailure',
215
+ 'NamespaceContentRemaining',
216
+ 'NamespaceFinalizersRemaining',
209
217
  // Core K8s — workloads
210
218
  'ReplicaFailure',
211
219
  // NPD — upstream default configs
@@ -220,6 +228,9 @@ const NEGATIVE_POLARITY_TYPES = new Set([
220
228
  'ReadOnlyRootFileSystem',
221
229
  'ResourceExhausted',
222
230
  'Swap',
231
+ 'StoragePressureRootFileSystem',
232
+ 'DPv2MigrationUnsupportedCNI',
233
+ 'UnsupportedEBPFPrograms',
223
234
  // NPD — AKS extensions (learn.microsoft.com/azure/aks/node-problem-detector)
224
235
  'FilesystemCorruptionProblem',
225
236
  'KubeletProblem',
@@ -382,6 +393,8 @@ export function ConditionsSection({
382
393
  // ============================================================================
383
394
 
384
395
  const ALERT_COLORS = {
396
+ alert: { bg: 'status-alert', border: '', title: '', message: '', list: '', bullet: '' },
397
+ unknown: { bg: 'status-unknown', border: '', title: '', message: '', list: '', bullet: '' },
385
398
  error: { bg: 'bg-red-50 dark:bg-red-950/30', border: 'border-red-200 dark:border-red-800/30', title: 'text-red-700 dark:text-red-300', message: 'text-red-600/80 dark:text-red-300/70', list: 'text-red-600 dark:text-red-400', bullet: 'text-red-400/60 dark:text-red-400/50' },
386
399
  warning: { bg: 'bg-amber-50 dark:bg-amber-950/30', border: 'border-amber-200 dark:border-amber-800/30', title: 'text-amber-700 dark:text-amber-300', message: 'text-amber-600/80 dark:text-amber-300/70', list: 'text-amber-600 dark:text-amber-400', bullet: 'text-amber-400/60 dark:text-amber-400/50' },
387
400
  info: { bg: 'bg-sky-50 dark:bg-sky-950/30', border: 'border-sky-200 dark:border-sky-800/30', title: 'text-sky-700 dark:text-sky-300', message: 'text-sky-600/80 dark:text-sky-300/70', list: 'text-sky-600 dark:text-sky-400', bullet: 'text-sky-400/60 dark:text-sky-400/50' },
@@ -389,6 +402,8 @@ const ALERT_COLORS = {
389
402
  } as const
390
403
 
391
404
  const DEFAULT_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
405
+ alert: AlertTriangle,
406
+ unknown: AlertTriangle,
392
407
  error: AlertTriangle,
393
408
  warning: AlertTriangle,
394
409
  info: AlertTriangle,
@@ -396,26 +411,37 @@ const DEFAULT_ICONS: Record<string, React.ComponentType<{ className?: string }>>
396
411
  }
397
412
 
398
413
  interface AlertBannerProps {
399
- variant: 'error' | 'warning' | 'info' | 'success'
414
+ variant: keyof typeof ALERT_COLORS
400
415
  icon?: React.ComponentType<{ className?: string }>
401
416
  title: string
402
417
  message?: React.ReactNode
403
418
  items?: string[]
404
419
  children?: React.ReactNode
420
+ /** A control at the end of the title line, e.g. a link to where the problem is handled. */
421
+ action?: React.ReactNode
422
+ /** Replaces the default bottom margin (`mb-4`). */
423
+ className?: string
405
424
  }
406
425
 
407
- export function AlertBanner({ variant, icon, title, message, items, children }: AlertBannerProps) {
426
+ export function AlertBanner({ variant, icon, title, message, items, children, action, className }: AlertBannerProps) {
408
427
  const colors = ALERT_COLORS[variant]
409
428
  const Icon = icon || DEFAULT_ICONS[variant]
410
429
  const hasBody = message || items || children
411
430
 
412
431
  return (
413
- <div className={clsx('mb-4 p-3 border rounded-lg', colors.bg, colors.border)}>
432
+ <div className={clsx('p-3 border rounded-lg', className ?? 'mb-4', colors.bg, colors.border)}>
414
433
  <div className={clsx('flex gap-2', hasBody ? 'items-start' : 'items-center')}>
415
434
  <Icon className={clsx('w-4 h-4 shrink-0', colors.title, hasBody && 'mt-0.5')} />
416
435
  {hasBody ? (
417
436
  <div className="flex-1 min-w-0">
418
- <div className={clsx('text-sm font-medium', colors.title, items && 'mb-1')}>{title}</div>
437
+ {action ? (
438
+ <div className={clsx('flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1', items && 'mb-1')}>
439
+ <div className={clsx('min-w-0 text-sm font-medium', colors.title)}>{title}</div>
440
+ <div className="shrink-0">{action}</div>
441
+ </div>
442
+ ) : (
443
+ <div className={clsx('text-sm font-medium', colors.title, items && 'mb-1')}>{title}</div>
444
+ )}
419
445
  {message && <div className={clsx('text-xs mt-1 break-words', colors.message)}>{message}</div>}
420
446
  {items && items.length > 0 && (
421
447
  <ul className={clsx('text-xs space-y-1', colors.list)}>
@@ -429,6 +455,11 @@ export function AlertBanner({ variant, icon, title, message, items, children }:
429
455
  )}
430
456
  {children}
431
457
  </div>
458
+ ) : action ? (
459
+ <div className="flex min-w-0 flex-1 flex-wrap items-center justify-between gap-x-3 gap-y-1">
460
+ <div className={clsx('min-w-0 text-sm font-medium', colors.title)}>{title}</div>
461
+ <div className="shrink-0">{action}</div>
462
+ </div>
432
463
  ) : (
433
464
  <div className={clsx('text-sm font-medium', colors.title)}>{title}</div>
434
465
  )}
@@ -515,19 +546,22 @@ export function AnnotationsSection({ data }: { data: any }) {
515
546
  )
516
547
  }
517
548
 
518
- export function MetadataSection({ data }: { data: any }) {
549
+ export function MetadataSection({ data, onNavigate }: { data: any; onNavigate?: (ref: ResourceRef) => void }) {
550
+ useSyncExternalStore(subscribeCRDDefinitions, getCRDDefinitionsSnapshot, getCRDDefinitionsSnapshot)
519
551
  const meta = data.metadata
520
552
  if (!meta) return null
553
+ const definition = customResourceDefinitionRef(data.apiVersion, data.kind)
521
554
 
522
555
  return (
523
556
  <Section title="Metadata" defaultExpanded>
524
557
  <PropertyList>
558
+ {definition && <Property label="Definition" value={<ResourceLink {...definition} onNavigate={onNavigate} />} />}
525
559
  <Property label="UID" value={meta.uid} />
526
560
  <Property label="Resource Version" value={meta.resourceVersion} />
527
561
  <Property label="Generation" value={meta.generation} />
528
562
  <Property label="Created" value={meta.creationTimestamp ? (
529
563
  <Tooltip content={new Date(meta.creationTimestamp).toLocaleString()}>
530
- <span className="border-b border-dotted border-theme-text-tertiary cursor-help">{formatAge(meta.creationTimestamp)}</span>
564
+ <span className="border-b border-dotted border-theme-text-tertiary cursor-help">{formatRelativeAgeTime(meta.creationTimestamp)}</span>
531
565
  </Tooltip>
532
566
  ) : '-'} />
533
567
  </PropertyList>
@@ -727,7 +761,7 @@ export function formatKindName(kind: string): string {
727
761
  pods: 'Pod', deployments: 'Deployment', daemonsets: 'DaemonSet', statefulsets: 'StatefulSet',
728
762
  replicasets: 'ReplicaSet', services: 'Service', endpointslices: 'EndpointSlice', ingresses: 'Ingress',
729
763
  gateways: 'Gateway', httproutes: 'HTTPRoute', grpcroutes: 'GRPCRoute',
730
- tcproutes: 'TCPRoute', tlsroutes: 'TLSRoute', configmaps: 'ConfigMap',
764
+ tcproutes: 'TCPRoute', tlsroutes: 'TLSRoute', udproutes: 'UDPRoute', configmaps: 'ConfigMap',
731
765
  secrets: 'Secret', jobs: 'Job', cronjobs: 'CronJob', hpas: 'HPA',
732
766
  horizontalpodautoscalers: 'HPA', nodes: 'Node', namespaces: 'Namespace',
733
767
  persistentvolumeclaims: 'PVC', persistentvolumes: 'PV',
@@ -784,25 +818,19 @@ export type CopyHandler = (text: string, key: string) => void
784
818
  import type { TimelineEvent, Relationships, ResourceRef } from '../../types'
785
819
  import { isChangeEvent, isK8sEvent } from '../../types'
786
820
  import { Link } from 'lucide-react'
821
+ import { formatRelativeAgeTime } from '../../utils/format'
787
822
 
788
823
  interface RelatedResourcesSectionProps {
789
824
  relationships: Relationships | undefined
790
825
  onNavigate?: (ref: ResourceRef) => void
791
826
  }
792
827
 
793
- function dedupeRefs(refs: ResourceRef[]): ResourceRef[] {
794
- const seen = new Set<string>()
795
- return refs.filter(ref => {
796
- const key = `${ref.kind}/${ref.namespace}/${ref.name}`
797
- if (seen.has(key)) return false
798
- seen.add(key)
799
- return true
800
- })
801
- }
802
-
803
828
  export function RelatedResourcesSection({ relationships, onNavigate }: RelatedResourcesSectionProps) {
804
829
  if (!relationships) return null
805
830
 
831
+ const configRefs = omitResourceRefs(relationships.configRefs ?? [], relationships.dependencies ?? [])
832
+ const consumers = omitResourceRefs(relationships.consumers ?? [], relationships.dependents ?? [])
833
+
806
834
  const hasRelationships =
807
835
  relationships.owner ||
808
836
  relationships.deployment ||
@@ -820,6 +848,15 @@ export function RelatedResourcesSection({ relationships, onNavigate }: RelatedRe
820
848
  (relationships.pdbs && relationships.pdbs.length > 0) ||
821
849
  (relationships.networkPolicies && relationships.networkPolicies.length > 0) ||
822
850
  (relationships.resourceClaims && relationships.resourceClaims.length > 0) ||
851
+ (relationships.dependencies && relationships.dependencies.length > 0) ||
852
+ (relationships.dependents && relationships.dependents.length > 0) ||
853
+ (relationships.monitors && relationships.monitors.length > 0) ||
854
+ (relationships.monitorTargets && relationships.monitorTargets.length > 0) ||
855
+ (relationships.backends && relationships.backends.length > 0) ||
856
+ (relationships.routedFrom && relationships.routedFrom.length > 0) ||
857
+ (relationships.protects && relationships.protects.length > 0) ||
858
+ (relationships.healthChecks && relationships.healthChecks.length > 0) ||
859
+ (relationships.stagedPolicies && relationships.stagedPolicies.length > 0) ||
823
860
  relationships.scaleTarget
824
861
 
825
862
  if (!hasRelationships) return null
@@ -837,43 +874,70 @@ export function RelatedResourcesSection({ relationships, onNavigate }: RelatedRe
837
874
  <RelationshipGroup label="Node" refs={[relationships.node]} onNavigate={onNavigate} />
838
875
  )}
839
876
  {relationships.children && relationships.children.length > 0 && (
840
- <RelationshipGroup label="Children" refs={dedupeRefs(relationships.children)} onNavigate={onNavigate} />
877
+ <RelationshipGroup label="Children" refs={dedupeResourceRefs(relationships.children)} onNavigate={onNavigate} />
841
878
  )}
842
879
  {relationships.services && relationships.services.length > 0 && (
843
- <RelationshipGroup label="Services" refs={dedupeRefs(relationships.services)} onNavigate={onNavigate} />
880
+ <RelationshipGroup label="Services" refs={dedupeResourceRefs(relationships.services)} onNavigate={onNavigate} />
844
881
  )}
845
882
  {relationships.ingresses && relationships.ingresses.length > 0 && (
846
- <RelationshipGroup label="Ingresses" refs={dedupeRefs(relationships.ingresses)} onNavigate={onNavigate} />
883
+ <RelationshipGroup label="Ingresses" refs={dedupeResourceRefs(relationships.ingresses)} onNavigate={onNavigate} />
847
884
  )}
848
885
  {relationships.gateways && relationships.gateways.length > 0 && (
849
- <RelationshipGroup label="Gateways" refs={dedupeRefs(relationships.gateways)} onNavigate={onNavigate} />
886
+ <RelationshipGroup label="Gateways" refs={dedupeResourceRefs(relationships.gateways)} onNavigate={onNavigate} />
850
887
  )}
851
888
  {relationships.routes && relationships.routes.length > 0 && (
852
- <RelationshipGroup label="Routes" refs={dedupeRefs(relationships.routes)} onNavigate={onNavigate} />
889
+ <RelationshipGroup label="Routes" refs={dedupeResourceRefs(relationships.routes)} onNavigate={onNavigate} />
853
890
  )}
854
891
  {relationships.pods && relationships.pods.length > 0 && (
855
- <RelationshipGroup label="Pods" refs={dedupeRefs(relationships.pods)} onNavigate={onNavigate} />
892
+ <RelationshipGroup label="Pods" refs={dedupeResourceRefs(relationships.pods)} onNavigate={onNavigate} />
856
893
  )}
857
- {relationships.configRefs && relationships.configRefs.length > 0 && (
858
- <RelationshipGroup label="Configuration" refs={dedupeRefs(relationships.configRefs)} onNavigate={onNavigate} />
894
+ {configRefs.length > 0 && (
895
+ <RelationshipGroup label="Configuration" refs={dedupeResourceRefs(configRefs)} onNavigate={onNavigate} />
859
896
  )}
860
- {relationships.consumers && relationships.consumers.length > 0 && (
861
- <RelationshipGroup label="Used By" refs={dedupeRefs(relationships.consumers)} onNavigate={onNavigate} />
897
+ {consumers.length > 0 && (
898
+ <RelationshipGroup label="Used By" refs={dedupeResourceRefs(consumers)} onNavigate={onNavigate} />
862
899
  )}
863
900
  {relationships.scalers && relationships.scalers.length > 0 && (
864
- <RelationshipGroup label="Autoscaler" refs={dedupeRefs(relationships.scalers)} onNavigate={onNavigate} />
901
+ <RelationshipGroup label="Autoscaler" refs={dedupeResourceRefs(relationships.scalers)} onNavigate={onNavigate} />
865
902
  )}
866
903
  {relationships.storageRefs && relationships.storageRefs.length > 0 && (
867
- <RelationshipGroup label="Storage" refs={dedupeRefs(relationships.storageRefs)} onNavigate={onNavigate} />
904
+ <RelationshipGroup label="Storage" refs={dedupeResourceRefs(relationships.storageRefs)} onNavigate={onNavigate} />
868
905
  )}
869
906
  {relationships.pdbs && relationships.pdbs.length > 0 && (
870
- <RelationshipGroup label="Disruption Budget" refs={dedupeRefs(relationships.pdbs)} onNavigate={onNavigate} />
907
+ <RelationshipGroup label="Disruption Budget" refs={dedupeResourceRefs(relationships.pdbs)} onNavigate={onNavigate} />
871
908
  )}
872
909
  {relationships.networkPolicies && relationships.networkPolicies.length > 0 && (
873
- <RelationshipGroup label="Network Policies" refs={dedupeRefs(relationships.networkPolicies)} onNavigate={onNavigate} />
910
+ <RelationshipGroup label="Network Policies" refs={dedupeResourceRefs(relationships.networkPolicies)} onNavigate={onNavigate} />
874
911
  )}
875
912
  {relationships.resourceClaims && relationships.resourceClaims.length > 0 && (
876
- <RelationshipGroup label="Resource Claims" refs={dedupeRefs(relationships.resourceClaims)} onNavigate={onNavigate} />
913
+ <RelationshipGroup label="Resource Claims" refs={dedupeResourceRefs(relationships.resourceClaims)} onNavigate={onNavigate} />
914
+ )}
915
+ {relationships.dependencies && relationships.dependencies.length > 0 && (
916
+ <RelationshipGroup label="Depends On" refs={dedupeResourceRefs(relationships.dependencies)} onNavigate={onNavigate} />
917
+ )}
918
+ {relationships.dependents && relationships.dependents.length > 0 && (
919
+ <RelationshipGroup label="Required By" refs={dedupeResourceRefs(relationships.dependents)} onNavigate={onNavigate} />
920
+ )}
921
+ {relationships.monitors && relationships.monitors.length > 0 && (
922
+ <RelationshipGroup label="Monitored By" refs={dedupeResourceRefs(relationships.monitors)} onNavigate={onNavigate} />
923
+ )}
924
+ {relationships.monitorTargets && relationships.monitorTargets.length > 0 && (
925
+ <RelationshipGroup label="Monitors" refs={dedupeResourceRefs(relationships.monitorTargets)} onNavigate={onNavigate} />
926
+ )}
927
+ {relationships.backends && relationships.backends.length > 0 && (
928
+ <RelationshipGroup label="Routes To" refs={dedupeResourceRefs(relationships.backends)} onNavigate={onNavigate} />
929
+ )}
930
+ {relationships.routedFrom && relationships.routedFrom.length > 0 && (
931
+ <RelationshipGroup label="Routed From" refs={dedupeResourceRefs(relationships.routedFrom)} onNavigate={onNavigate} />
932
+ )}
933
+ {relationships.protects && relationships.protects.length > 0 && (
934
+ <RelationshipGroup label="Selects" refs={dedupeResourceRefs(relationships.protects)} onNavigate={onNavigate} />
935
+ )}
936
+ {relationships.healthChecks && relationships.healthChecks.length > 0 && (
937
+ <RelationshipGroup label="Health Checks" refs={dedupeResourceRefs(relationships.healthChecks)} onNavigate={onNavigate} />
938
+ )}
939
+ {relationships.stagedPolicies && relationships.stagedPolicies.length > 0 && (
940
+ <RelationshipGroup label="Staged Policies" refs={dedupeResourceRefs(relationships.stagedPolicies)} onNavigate={onNavigate} />
877
941
  )}
878
942
  {relationships.scaleTarget && (
879
943
  <RelationshipGroup label="Scale Target" refs={[relationships.scaleTarget]} onNavigate={onNavigate} />
@@ -926,7 +990,7 @@ export interface ResourceRefBadgeProps {
926
990
  /** Reusable chip/badge for showing a related resource with click-to-navigate */
927
991
  export function ResourceRefBadge({ resourceRef, onClick, wrapAtSeparator }: ResourceRefBadgeProps) {
928
992
  const kindClass = getKindColor(resourceRef.kind)
929
- const kindName = formatKindForRef(resourceRef.kind)
993
+ const kindName = formatKindForRef(resourceRef.kind, resourceRef.group)
930
994
  const content = wrapAtSeparator ? (
931
995
  <>
932
996
  <span className="shrink-0 opacity-60">{kindName}/</span>
@@ -989,8 +1053,10 @@ export function ResourceLink({ name, kind, namespace = '', group, label, onNavig
989
1053
  )
990
1054
  }
991
1055
 
992
- function formatKindForRef(kind: string): string {
1056
+ function formatKindForRef(kind: string, group?: string): string {
993
1057
  const k = kind.toLowerCase()
1058
+ // A Knative Service shares its kind with core Services; use Knative's own short name.
1059
+ if (k === 'service' && group === 'serving.knative.dev') return 'ksvc'
994
1060
  const shortNames: Record<string, string> = {
995
1061
  deployment: 'deploy',
996
1062
  daemonset: 'ds',
@@ -1003,6 +1069,7 @@ function formatKindForRef(kind: string): string {
1003
1069
  httproute: 'hr',
1004
1070
  grpcroute: 'grpc',
1005
1071
  tcproute: 'tcp',
1072
+ udproute: 'udp',
1006
1073
  tlsroute: 'tls',
1007
1074
  secret: 'secret',
1008
1075
  pod: 'pod',
@@ -19,7 +19,9 @@ export type { SelectMenuOption } from './SelectMenu'
19
19
  export { Input } from './Input'
20
20
  export { FilterPill } from './FilterPill'
21
21
  export type { FilterPillTone } from './FilterPill'
22
- export { StatusDot, mapHealthToTone } from './status-tone'
22
+ export { StatusDot, mapHealthToTone, toneFillClass, toneTextClass, worseTone } from './status-tone'
23
+ export { RefLink, type NavigateToRef } from './RefLink'
24
+ export { FoldSection, SectionHeading, type FoldSummary } from './FoldSection'
23
25
  export type { StatusTone, StatusDotProps } from './status-tone'
24
26
  export { DialogPortal } from './DialogPortal'
25
27
  export { ConfirmDialog } from './ConfirmDialog'
@@ -0,0 +1,77 @@
1
+ // Every editor feature monaco-editor's editor.main entry registers (context menu,
2
+ // comments, multi-cursor, line operations, bracket matching, sticky scroll, quick
3
+ // access, ...), without its language definitions and the TS/JSON/CSS/HTML language
4
+ // services, whose workers this runtime does not ship. editor.api alone registers none
5
+ // of these, so dropping this import silently strips them from every editor.
6
+ import 'monaco-editor/editor/contrib/anchorSelect/browser/anchorSelect.js'
7
+ import 'monaco-editor/editor/contrib/bracketMatching/browser/bracketMatching.js'
8
+ import 'monaco-editor/editor/contrib/caretOperations/browser/transpose.js'
9
+ import 'monaco-editor/editor/contrib/clipboard/browser/clipboard.js'
10
+ import 'monaco-editor/editor/contrib/codeAction/browser/codeActionContributions.js'
11
+ import 'monaco-editor/editor/browser/widget/codeEditor/codeEditorWidget.js'
12
+ import 'monaco-editor/editor/contrib/codelens/browser/codelensController.js'
13
+ import 'monaco-editor/editor/contrib/colorPicker/browser/colorPickerContribution.js'
14
+ import 'monaco-editor/editor/contrib/comment/browser/comment.js'
15
+ import 'monaco-editor/editor/contrib/contextmenu/browser/contextmenu.js'
16
+ import 'monaco-editor/editor/contrib/cursorUndo/browser/cursorUndo.js'
17
+ import 'monaco-editor/editor/browser/widget/diffEditor/diffEditor.contribution.js'
18
+ import 'monaco-editor/editor/contrib/diffEditorBreadcrumbs/browser/contribution.js'
19
+ import 'monaco-editor/editor/contrib/dnd/browser/dnd.js'
20
+ import 'monaco-editor/editor/contrib/documentSymbols/browser/documentSymbols.js'
21
+ import 'monaco-editor/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution.js'
22
+ import 'monaco-editor/features/find/register.js'
23
+ import 'monaco-editor/editor/contrib/floatingMenu/browser/floatingMenu.contribution.js'
24
+ import 'monaco-editor/editor/contrib/folding/browser/folding.js'
25
+ import 'monaco-editor/editor/contrib/fontZoom/browser/fontZoom.js'
26
+ import 'monaco-editor/editor/contrib/format/browser/formatActions.js'
27
+ import 'monaco-editor/editor/contrib/gotoError/browser/gotoError.js'
28
+ import 'monaco-editor/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.js'
29
+ import 'monaco-editor/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.js'
30
+ import 'monaco-editor/editor/contrib/gpu/browser/gpuActions.js'
31
+ import 'monaco-editor/editor/contrib/hover/browser/hoverContribution.js'
32
+ import 'monaco-editor/editor/contrib/indentation/browser/indentation.js'
33
+ import 'monaco-editor/editor/contrib/inlayHints/browser/inlayHintsContribution.js'
34
+ import 'monaco-editor/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.js'
35
+ import 'monaco-editor/editor/contrib/inlineProgress/browser/inlineProgress.js'
36
+ import 'monaco-editor/editor/contrib/inPlaceReplace/browser/inPlaceReplace.js'
37
+ import 'monaco-editor/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.js'
38
+ import 'monaco-editor/editor/standalone/browser/inspectTokens/inspectTokens.js'
39
+ import 'monaco-editor/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.js'
40
+ import 'monaco-editor/editor/contrib/lineSelection/browser/lineSelection.js'
41
+ import 'monaco-editor/editor/contrib/linesOperations/browser/linesOperations.js'
42
+ import 'monaco-editor/editor/contrib/linkedEditing/browser/linkedEditing.js'
43
+ import 'monaco-editor/editor/contrib/links/browser/links.js'
44
+ import 'monaco-editor/editor/contrib/longLinesHelper/browser/longLinesHelper.js'
45
+ import 'monaco-editor/editor/contrib/middleScroll/browser/middleScroll.contribution.js'
46
+ import 'monaco-editor/editor/contrib/multicursor/browser/multicursor.js'
47
+ import 'monaco-editor/editor/contrib/parameterHints/browser/parameterHints.js'
48
+ import 'monaco-editor/editor/contrib/placeholderText/browser/placeholderText.contribution.js'
49
+ import 'monaco-editor/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.js'
50
+ import 'monaco-editor/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.js'
51
+ import 'monaco-editor/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.js'
52
+ import 'monaco-editor/editor/contrib/readOnlyMessage/browser/contribution.js'
53
+ import 'monaco-editor/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.js'
54
+ import 'monaco-editor/editor/contrib/rename/browser/rename.js'
55
+ import 'monaco-editor/editor/contrib/sectionHeaders/browser/sectionHeaders.js'
56
+ import 'monaco-editor/editor/contrib/semanticTokens/browser/viewportSemanticTokens.js'
57
+ import 'monaco-editor/editor/contrib/smartSelect/browser/smartSelect.js'
58
+ import 'monaco-editor/editor/contrib/snippet/browser/snippetController2.js'
59
+ import 'monaco-editor/editor/contrib/stickyScroll/browser/stickyScrollContribution.js'
60
+ import 'monaco-editor/editor/contrib/suggest/browser/suggestInlineCompletions.js'
61
+ import 'monaco-editor/editor/standalone/browser/toggleHighContrast/toggleHighContrast.js'
62
+ import 'monaco-editor/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.js'
63
+ import 'monaco-editor/editor/contrib/tokenization/browser/tokenization.js'
64
+ import 'monaco-editor/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.js'
65
+ import 'monaco-editor/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.js'
66
+ import 'monaco-editor/editor/contrib/wordHighlighter/browser/wordHighlighter.js'
67
+ import 'monaco-editor/editor/contrib/wordOperations/browser/wordOperations.js'
68
+ import 'monaco-editor/editor/contrib/wordPartOperations/browser/wordPartOperations.js'
69
+ import 'monaco-editor/editor/browser/coreCommands.js'
70
+ import 'monaco-editor/editor/contrib/caretOperations/browser/caretOperations.js'
71
+ import 'monaco-editor/editor/contrib/dropOrPasteInto/browser/copyPasteContribution.js'
72
+ import 'monaco-editor/editor/contrib/find/browser/findController.js'
73
+ import 'monaco-editor/editor/contrib/gotoSymbol/browser/goToCommands.js'
74
+ import 'monaco-editor/editor/contrib/gotoError/browser/markerSelectionStatus.js'
75
+ import 'monaco-editor/editor/contrib/semanticTokens/browser/documentSemanticTokens.js'
76
+ import 'monaco-editor/editor/contrib/suggest/browser/suggestController.js'
77
+ import 'monaco-editor/editor/common/standaloneStrings.js'
@@ -1,28 +1,23 @@
1
1
  /// <reference path="../../monaco-deep.d.ts" />
2
2
 
3
3
  import { loader } from '@monaco-editor/react'
4
- import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
4
+ import * as monaco from 'monaco-editor/editor/editor.api'
5
5
  // Tokenizers only — the set the YAML editor and the pod file viewer hand to
6
6
  // Monaco. JSON deliberately absent: its language lives in vs/language/json and
7
7
  // spawns a validation worker this runtime does not ship; viewers highlight
8
8
  // JSON with the YAML tokenizer, which covers it.
9
- import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'
10
- import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution'
11
- import 'monaco-editor/esm/vs/basic-languages/html/html.contribution'
12
- import 'monaco-editor/esm/vs/basic-languages/css/css.contribution'
13
- import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
14
- import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
15
- import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'
16
- import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'
17
- import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'
18
- import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution'
19
- import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'
20
- import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js'
21
- import 'monaco-editor/esm/vs/editor/contrib/folding/browser/folding.js'
22
- import 'monaco-editor/esm/vs/editor/contrib/format/browser/formatActions.js'
23
- import 'monaco-editor/esm/vs/editor/contrib/gotoError/browser/gotoError.js'
24
- import 'monaco-editor/esm/vs/editor/contrib/hover/browser/hoverContribution.js'
25
- import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js'
9
+ import 'monaco-editor/languages/definitions/yaml/register'
10
+ import 'monaco-editor/languages/definitions/xml/register'
11
+ import 'monaco-editor/languages/definitions/html/register'
12
+ import 'monaco-editor/languages/definitions/css/register'
13
+ import 'monaco-editor/languages/definitions/javascript/register'
14
+ import 'monaco-editor/languages/definitions/typescript/register'
15
+ import 'monaco-editor/languages/definitions/python/register'
16
+ import 'monaco-editor/languages/definitions/shell/register'
17
+ import 'monaco-editor/languages/definitions/markdown/register'
18
+ import 'monaco-editor/languages/definitions/ini/register'
19
+ import 'monaco-editor/languages/definitions/dockerfile/register'
20
+ import './monacoEditorFeatures'
26
21
 
27
22
  type MonacoGlobal = typeof globalThis & {
28
23
  MonacoEnvironment?: {
@@ -37,7 +32,7 @@ export async function ensureMonaco() {
37
32
  ;(globalThis as MonacoGlobal).MonacoEnvironment = {
38
33
  getWorker() {
39
34
  return new Worker(
40
- new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url),
35
+ new URL('monaco-editor/editor/editor.worker.js', import.meta.url),
41
36
  {
42
37
  name: 'radar-monaco-editor',
43
38
  type: 'module',