@skyhook-io/k8s-ui 1.2.4 → 1.3.0

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 (119) hide show
  1. package/package.json +3 -2
  2. package/src/components/dock/BottomDock.tsx +1 -1
  3. package/src/components/dock/LocalTerminalTab.tsx +1 -1
  4. package/src/components/dock/NodeTerminalTab.tsx +1 -1
  5. package/src/components/dock/TerminalTab.tsx +2 -2
  6. package/src/components/gitops/GitOpsActions.tsx +12 -11
  7. package/src/components/gitops/GitOpsStatusBadge.tsx +6 -6
  8. package/src/components/logs/LogCore.tsx +8 -8
  9. package/src/components/logs/WorkloadLogsViewer.tsx +29 -5
  10. package/src/components/logs/useLogStream.ts +20 -0
  11. package/src/components/resources/ResourcesSidebar.tsx +10 -10
  12. package/src/components/resources/ResourcesView.tsx +49 -48
  13. package/src/components/resources/renderers/AlertRenderer.tsx +2 -2
  14. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +2 -2
  15. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -1
  16. package/src/components/resources/renderers/CertificateRenderer.tsx +3 -3
  17. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +3 -3
  18. package/src/components/resources/renderers/ChallengeRenderer.tsx +2 -2
  19. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +3 -3
  20. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +3 -3
  21. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -2
  22. package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +1 -1
  23. package/src/components/resources/renderers/EventRenderer.tsx +1 -1
  24. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -2
  25. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +2 -2
  26. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -1
  27. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +5 -5
  28. package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -1
  29. package/src/components/resources/renderers/GatewayRenderer.tsx +3 -3
  30. package/src/components/resources/renderers/GenericRenderer.tsx +1 -1
  31. package/src/components/resources/renderers/HPARenderer.tsx +1 -1
  32. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +5 -5
  33. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -1
  34. package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -1
  35. package/src/components/resources/renderers/IngressRenderer.tsx +1 -1
  36. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +10 -10
  37. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -1
  38. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +2 -2
  39. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +2 -2
  40. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +6 -6
  41. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -5
  42. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +5 -5
  43. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -2
  44. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +4 -4
  45. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +3 -3
  46. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +3 -3
  47. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +5 -5
  48. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +2 -2
  49. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +5 -5
  50. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +4 -4
  51. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +8 -8
  52. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +3 -3
  53. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -1
  54. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +2 -2
  55. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +6 -6
  56. package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -1
  57. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +4 -4
  58. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -6
  59. package/src/components/resources/renderers/NodeRenderer.tsx +5 -5
  60. package/src/components/resources/renderers/OrderRenderer.tsx +3 -3
  61. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -1
  62. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -1
  63. package/src/components/resources/renderers/PodMonitorRenderer.tsx +3 -3
  64. package/src/components/resources/renderers/PodRenderer.tsx +10 -10
  65. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +2 -2
  66. package/src/components/resources/renderers/RoleBindingRenderer.tsx +3 -3
  67. package/src/components/resources/renderers/RoleRenderer.tsx +8 -8
  68. package/src/components/resources/renderers/RolloutRenderer.tsx +1 -1
  69. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +2 -2
  70. package/src/components/resources/renderers/SealedSecretRenderer.tsx +2 -2
  71. package/src/components/resources/renderers/SecretRenderer.tsx +3 -3
  72. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -1
  73. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +3 -3
  74. package/src/components/resources/renderers/ServiceRenderer.tsx +1 -1
  75. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +5 -5
  76. package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -1
  77. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -1
  78. package/src/components/resources/renderers/VPARenderer.tsx +2 -2
  79. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +2 -2
  80. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +6 -6
  81. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +5 -5
  82. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +6 -6
  83. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -1
  84. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +10 -10
  85. package/src/components/resources/renderers/WorkflowRenderer.tsx +2 -2
  86. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +2 -2
  87. package/src/components/resources/renderers/WorkloadRenderer.tsx +1 -1
  88. package/src/components/resources/renderers/argo-cells.tsx +3 -3
  89. package/src/components/resources/renderers/certmanager-cells.tsx +6 -6
  90. package/src/components/resources/renderers/cnpg-cells.tsx +5 -5
  91. package/src/components/resources/renderers/eso-cells.tsx +4 -4
  92. package/src/components/resources/renderers/flux-cells.tsx +6 -6
  93. package/src/components/resources/renderers/istio-cells.tsx +9 -9
  94. package/src/components/resources/renderers/karpenter-cells.tsx +3 -3
  95. package/src/components/resources/renderers/keda-cells.tsx +2 -2
  96. package/src/components/resources/renderers/knative-cells.tsx +2 -2
  97. package/src/components/resources/renderers/kyverno-cells.tsx +3 -3
  98. package/src/components/resources/renderers/prometheus-cells.tsx +3 -3
  99. package/src/components/resources/renderers/trivy-cells.tsx +4 -4
  100. package/src/components/resources/renderers/velero-cells.tsx +4 -4
  101. package/src/components/shared/EditableYamlView.tsx +1 -1
  102. package/src/components/shared/ResourceActionsBar.tsx +17 -17
  103. package/src/components/shared/ResourceRendererDispatch.tsx +8 -7
  104. package/src/components/topology/K8sResourceNode.tsx +5 -5
  105. package/src/components/topology/TopologyControls.tsx +2 -2
  106. package/src/components/topology/TopologyGraph.tsx +89 -23
  107. package/src/components/topology/topology.css +1 -1
  108. package/src/components/ui/Badge.tsx +178 -0
  109. package/src/components/ui/HealthRing.tsx +70 -35
  110. package/src/components/ui/MetricsChart.tsx +2 -2
  111. package/src/components/ui/drawer-components.tsx +12 -31
  112. package/src/components/workload/ResourceDetailDrawer.tsx +4 -4
  113. package/src/components/workload/WorkloadView.tsx +10 -10
  114. package/src/theme/components.css +195 -0
  115. package/src/theme/tailwind-theme.css +13 -13
  116. package/src/theme/variables.css +46 -29
  117. package/src/types/core.ts +2 -0
  118. package/src/utils/badge-colors.ts +54 -205
  119. package/src/utils/log-format.ts +0 -8
@@ -116,6 +116,7 @@ import {
116
116
  parseColumnFilters,
117
117
  serializeColumnFilters,
118
118
  } from './resource-utils'
119
+ import { SEVERITY_BADGE, EVENT_TYPE_COLORS } from '../../utils/badge-colors'
119
120
  import { Tooltip } from '../ui/Tooltip'
120
121
  // CRD-specific cell components (extracted)
121
122
  import { GitRepositoryCell, OCIRepositoryCell, HelmRepositoryCell, KustomizationCell, FluxHelmReleaseCell, FluxAlertCell } from './renderers/flux-cells'
@@ -2921,9 +2922,9 @@ export function ResourcesView({
2921
2922
  )}
2922
2923
 
2923
2924
  {/* Main Content - Resource Table */}
2924
- <div className="flex-1 flex flex-col overflow-hidden min-w-0">
2925
+ <div className="flex-1 flex flex-col overflow-hidden min-w-0 bg-theme-surface">
2925
2926
  {/* Toolbar */}
2926
- <div className="flex items-center gap-3 px-4 py-3 border-b border-theme-border bg-theme-surface/50 shrink-0">
2927
+ <div className="flex items-center gap-3 px-4 py-3 border-b border-theme-border bg-theme-base shrink-0">
2927
2928
  <div className="flex-1 relative">
2928
2929
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-theme-text-tertiary" />
2929
2930
  <input
@@ -2932,7 +2933,7 @@ export function ResourcesView({
2932
2933
  placeholder="Search... (press /)"
2933
2934
  value={searchTerm}
2934
2935
  onChange={(e) => setSearchTerm(e.target.value)}
2935
- className="w-full max-w-md pl-10 pr-4 py-2 bg-theme-elevated border border-theme-border-light rounded-lg text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2 focus:ring-blue-500"
2936
+ className="w-full max-w-md pl-10 pr-4 py-2 bg-theme-elevated border border-theme-border-light rounded-lg text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2 focus:ring-skyhook-500"
2936
2937
  />
2937
2938
  </div>
2938
2939
 
@@ -2944,14 +2945,14 @@ export function ResourcesView({
2944
2945
  className={clsx(
2945
2946
  'flex items-center gap-1.5 px-2.5 py-2 rounded-lg text-xs transition-colors',
2946
2947
  problemFilters.length > 0
2947
- ? 'bg-red-500/20 text-red-700 dark:text-red-300 hover:bg-red-500/30'
2948
+ ? `${SEVERITY_BADGE.error} hover:bg-red-500/30`
2948
2949
  : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
2949
2950
  )}
2950
2951
  >
2951
2952
  <AlertTriangle className="w-3.5 h-3.5" />
2952
2953
  <span>Problems</span>
2953
2954
  {problemFilters.length > 0 && (
2954
- <span className="px-1.5 py-0.5 text-xs bg-red-500/30 text-red-700 dark:text-red-300 rounded">
2955
+ <span className={clsx('badge-sm', SEVERITY_BADGE.error)}>
2955
2956
  {problemFilters.length}
2956
2957
  </span>
2957
2958
  )}
@@ -2972,7 +2973,7 @@ export function ResourcesView({
2972
2973
  className={clsx(
2973
2974
  'w-full text-left px-3 py-1.5 text-xs flex items-center justify-between gap-2 transition-colors',
2974
2975
  problemFilters.includes(value)
2975
- ? 'bg-red-500/20 text-red-700 dark:text-red-300'
2976
+ ? SEVERITY_BADGE.error
2976
2977
  : 'text-theme-text-secondary hover:bg-theme-elevated hover:text-theme-text-primary'
2977
2978
  )}
2978
2979
  >
@@ -2994,14 +2995,14 @@ export function ResourcesView({
2994
2995
  className={clsx(
2995
2996
  'flex items-center gap-1.5 px-2.5 py-2 rounded-lg text-xs transition-colors',
2996
2997
  activeLabelPairs.length > 0
2997
- ? 'bg-green-500/20 text-green-700 dark:text-green-300 hover:bg-green-500/30'
2998
+ ? `${SEVERITY_BADGE.success} hover:bg-emerald-500/30`
2998
2999
  : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
2999
3000
  )}
3000
3001
  >
3001
3002
  <Tag className="w-3.5 h-3.5" />
3002
3003
  <span>Labels</span>
3003
3004
  {activeLabelPairs.length > 0 && (
3004
- <span className="px-1.5 py-0.5 text-xs bg-green-500/30 text-green-700 dark:text-green-300 rounded">
3005
+ <span className={clsx('badge-sm', SEVERITY_BADGE.success)}>
3005
3006
  {activeLabelPairs.length}
3006
3007
  </span>
3007
3008
  )}
@@ -3022,7 +3023,7 @@ export function ResourcesView({
3022
3023
  value={labelSearch}
3023
3024
  onChange={(e) => setLabelSearch(e.target.value)}
3024
3025
  autoFocus
3025
- className="w-full pl-7 pr-2 py-1.5 text-xs bg-theme-elevated border border-theme-border-light rounded text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-1 focus:ring-blue-500"
3026
+ className="w-full pl-7 pr-2 py-1.5 text-xs bg-theme-elevated border border-theme-border-light rounded text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-1 focus:ring-skyhook-500"
3026
3027
  />
3027
3028
  </div>
3028
3029
  {activeLabelPairs.length > 0 && (
@@ -3037,7 +3038,7 @@ export function ResourcesView({
3037
3038
  className={clsx(
3038
3039
  'w-full text-left px-3 py-1.5 text-xs flex items-center justify-between gap-2 transition-colors',
3039
3040
  activeLabelPairs.includes(value)
3040
- ? 'bg-green-500/20 text-green-700 dark:text-green-300'
3041
+ ? SEVERITY_BADGE.success
3041
3042
  : 'text-theme-text-secondary hover:bg-theme-elevated hover:text-theme-text-primary'
3042
3043
  )}
3043
3044
  >
@@ -3062,7 +3063,7 @@ export function ResourcesView({
3062
3063
  type="checkbox"
3063
3064
  checked={showInactiveReplicaSets}
3064
3065
  onChange={(e) => setShowInactiveReplicaSets(e.target.checked)}
3065
- className="w-3 h-3 rounded border-theme-border-light accent-blue-500"
3066
+ className="w-3 h-3 rounded border-theme-border-light accent-skyhook-500"
3066
3067
  />
3067
3068
  Show inactive ({inactiveReplicaSetCount})
3068
3069
  </label>
@@ -3070,7 +3071,7 @@ export function ResourcesView({
3070
3071
 
3071
3072
  {/* Active filter badges — owner only (column filters shown on header, problems/labels on their buttons) */}
3072
3073
  {hasOwnerFilter && (
3073
- <span className="flex items-center gap-1 px-2 py-1 text-xs bg-purple-500/20 text-purple-700 dark:text-purple-300 rounded">
3074
+ <span className={clsx('flex items-center gap-1 px-2 py-1 text-xs rounded', SEVERITY_BADGE.info)}>
3074
3075
  {ownerKind}: {ownerName}
3075
3076
  <button
3076
3077
  onClick={() => {
@@ -3201,7 +3202,7 @@ export function ResourcesView({
3201
3202
  <button
3202
3203
  key={key}
3203
3204
  onClick={() => clearColumnFilter(key)}
3204
- className="flex items-center gap-1 px-2 py-1 text-xs bg-blue-500/15 text-blue-700 dark:text-blue-300 rounded-md hover:bg-blue-500/25 transition-colors"
3205
+ className="flex items-center gap-1 px-2 py-1 text-xs selection selection-text rounded-md hover:selection-strong transition-colors"
3205
3206
  >
3206
3207
  <ListFilter className="w-3 h-3" />
3207
3208
  <span>{key}: {vals.join(', ')}</span>
@@ -3256,7 +3257,7 @@ export function ResourcesView({
3256
3257
  key={col.key}
3257
3258
  className={clsx(
3258
3259
  'text-left px-4 py-3 text-xs font-medium uppercase tracking-wide relative group/th',
3259
- 'bg-theme-surface border-b border-theme-border',
3260
+ 'bg-theme-base border-b border-theme-border',
3260
3261
  !isLastCol && 'border-r-subtle',
3261
3262
  isSortable ? 'text-theme-text-secondary hover:text-theme-text-primary cursor-pointer select-none' : 'text-theme-text-secondary'
3262
3263
  )}
@@ -3299,7 +3300,7 @@ export function ResourcesView({
3299
3300
  className={clsx(
3300
3301
  'rounded-l transition-colors flex items-center gap-0.5',
3301
3302
  hasActiveFilter
3302
- ? 'px-1.5 py-0.5 -my-0.5 bg-blue-500/20 text-blue-700 dark:text-blue-300 hover:bg-blue-500/30'
3303
+ ? 'px-1.5 py-0.5 -my-0.5 selection-strong selection-text hover:bg-skyhook-500/30'
3303
3304
  : isFilterOpen
3304
3305
  ? 'p-0.5 text-theme-text-primary'
3305
3306
  : 'p-0.5 text-theme-text-disabled opacity-0 group-hover/th:opacity-100 hover:text-theme-text-primary'
@@ -3316,7 +3317,7 @@ export function ResourcesView({
3316
3317
  clearColumnFilter(col.key)
3317
3318
  setOpenColumnFilter(null)
3318
3319
  }}
3319
- className="rounded-r px-0.5 py-0.5 -my-0.5 bg-blue-500/20 text-blue-700 dark:text-blue-300 hover:bg-blue-500/30 transition-colors"
3320
+ className="rounded-r px-0.5 py-0.5 -my-0.5 selection-strong selection-text hover:bg-skyhook-500/30 transition-colors"
3320
3321
  title="Clear filter"
3321
3322
  >
3322
3323
  <X className="w-3 h-3" />
@@ -3350,7 +3351,7 @@ export function ResourcesView({
3350
3351
  value={columnFilterSearch}
3351
3352
  onChange={(e) => setColumnFilterSearch(e.target.value)}
3352
3353
  autoFocus
3353
- className="w-full pl-7 pr-2 py-1.5 text-xs bg-theme-elevated border border-theme-border-light rounded text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-1 focus:ring-blue-500"
3354
+ className="w-full pl-7 pr-2 py-1.5 text-xs bg-theme-elevated border border-theme-border-light rounded text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-1 focus:ring-skyhook-500"
3354
3355
  />
3355
3356
  </div>
3356
3357
  {activeFilterValues.length > 0 && (
@@ -3373,11 +3374,11 @@ export function ResourcesView({
3373
3374
  className={clsx(
3374
3375
  'w-full text-left px-3 py-1.5 text-xs flex items-center gap-2 transition-colors',
3375
3376
  isSelected
3376
- ? 'bg-blue-500/20 text-blue-700 dark:text-blue-300'
3377
+ ? 'selection-strong selection-text'
3377
3378
  : 'text-theme-text-secondary hover:bg-theme-elevated hover:text-theme-text-primary'
3378
3379
  )}
3379
3380
  >
3380
- <span className={clsx('w-3 h-3 shrink-0 rounded-sm border flex items-center justify-center', isSelected ? 'bg-blue-500 border-blue-500' : 'border-theme-border')}>
3381
+ <span className={clsx('w-3 h-3 shrink-0 rounded-sm border flex items-center justify-center', isSelected ? 'bg-skyhook-500 border-skyhook-500' : 'border-theme-border')}>
3381
3382
  {isSelected && <Check className="w-2 h-2 text-white" />}
3382
3383
  </span>
3383
3384
  <span className="truncate" title={value}>{value}</span>
@@ -3406,7 +3407,7 @@ export function ResourcesView({
3406
3407
  </th>
3407
3408
  )
3408
3409
  })}
3409
- {hasResizedColumns && <th className="bg-theme-surface border-b border-theme-border p-0" />}
3410
+ {hasResizedColumns && <th className="bg-theme-base border-b border-theme-border p-0" />}
3410
3411
  </tr>
3411
3412
  )}
3412
3413
  itemContent={(index, resource) => {
@@ -3437,7 +3438,7 @@ export function ResourcesView({
3437
3438
  {/* Resize indicator line — full table height, shown only while dragging */}
3438
3439
  {resizeLineX !== null && (
3439
3440
  <div
3440
- className="absolute top-0 bottom-0 w-0.5 bg-blue-500 pointer-events-none"
3441
+ className="absolute top-0 bottom-0 w-0.5 bg-skyhook-500 pointer-events-none"
3441
3442
  style={{ left: resizeLineX, zIndex: 20 }}
3442
3443
  />
3443
3444
  )}
@@ -3475,9 +3476,9 @@ function ResourceRowCells({ resource, kind, group, columns, hasSpacerColumn, isS
3475
3476
  'px-4 py-3 border-b-subtle cursor-pointer transition-colors',
3476
3477
  col.key !== 'status' && 'overflow-hidden truncate',
3477
3478
  isSelected
3478
- ? 'bg-blue-500/20 group-hover/row:bg-blue-500/30'
3479
+ ? 'selection-strong group-hover/row:bg-skyhook-500/30'
3479
3480
  : isHighlighted
3480
- ? 'bg-blue-500/10 ring-1 ring-inset ring-blue-400/30'
3481
+ ? 'selection selection-ring'
3481
3482
  : 'group-hover/row:bg-theme-surface/50'
3482
3483
  )}
3483
3484
  >
@@ -3846,7 +3847,7 @@ function GenericCell({ resource, column }: { resource: any; column: string }) {
3846
3847
  const isWarning = ['Pending', 'Progressing', 'Unknown'].includes(phase)
3847
3848
  return (
3848
3849
  <span className={clsx(
3849
- 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium',
3850
+ 'badge',
3850
3851
  isHealthy ? 'status-healthy' :
3851
3852
  isWarning ? 'status-degraded' :
3852
3853
  'status-unhealthy'
@@ -3863,7 +3864,7 @@ function GenericCell({ resource, column }: { resource: any; column: string }) {
3863
3864
  const isReady = readyCondition.status === 'True'
3864
3865
  return (
3865
3866
  <span className={clsx(
3866
- 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium',
3867
+ 'badge',
3867
3868
  isReady ? 'status-healthy' : 'status-degraded'
3868
3869
  )}>
3869
3870
  {isReady ? 'Ready' : 'Not Ready'}
@@ -4007,7 +4008,7 @@ function PodCell({ resource, column }: { resource: any; column: string }) {
4007
4008
  const problems = getPodProblems(resource)
4008
4009
  return (
4009
4010
  <div className="flex items-center gap-2">
4010
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4011
+ <span className={clsx('badge', status.color)}>
4011
4012
  {status.text}
4012
4013
  </span>
4013
4014
  {problems.length > 0 && (
@@ -4208,7 +4209,7 @@ function ReplicaSetCell({ resource, column }: { resource: any; column: string })
4208
4209
  const isActive = isReplicaSetActive(resource)
4209
4210
  return (
4210
4211
  <span className={clsx(
4211
- 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium',
4212
+ 'badge',
4212
4213
  isActive ? 'status-neutral' : 'status-unknown'
4213
4214
  )}>
4214
4215
  {isActive ? 'Active' : 'Old'}
@@ -4225,7 +4226,7 @@ function ServiceCell({ resource, column }: { resource: any; column: string }) {
4225
4226
  case 'type': {
4226
4227
  const status = getServiceStatus(resource)
4227
4228
  return (
4228
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4229
+ <span className={clsx('badge', status.color)}>
4229
4230
  {status.text}
4230
4231
  </span>
4231
4232
  )
@@ -4243,7 +4244,7 @@ function ServiceCell({ resource, column }: { resource: any; column: string }) {
4243
4244
  case 'endpoints': {
4244
4245
  const { status, color } = getServiceEndpointsStatus(resource)
4245
4246
  return (
4246
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', color)}>
4247
+ <span className={clsx('badge', color)}>
4247
4248
  {status}
4248
4249
  </span>
4249
4250
  )
@@ -4346,7 +4347,7 @@ function SecretCell({ resource, column }: { resource: any; column: string }) {
4346
4347
  case 'type': {
4347
4348
  const { type, color } = getSecretType(resource)
4348
4349
  return (
4349
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', color)}>
4350
+ <span className={clsx('badge', color)}>
4350
4351
  {type}
4351
4352
  </span>
4352
4353
  )
@@ -4385,7 +4386,7 @@ function JobCell({ resource, column }: { resource: any; column: string }) {
4385
4386
  case 'status': {
4386
4387
  const status = getJobStatus(resource)
4387
4388
  return (
4388
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4389
+ <span className={clsx('badge', status.color)}>
4389
4390
  {status.text}
4390
4391
  </span>
4391
4392
  )
@@ -4425,7 +4426,7 @@ function CronJobCell({ resource, column }: { resource: any; column: string }) {
4425
4426
  case 'status': {
4426
4427
  const status = getCronJobStatus(resource)
4427
4428
  return (
4428
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4429
+ <span className={clsx('badge', status.color)}>
4429
4430
  {status.text}
4430
4431
  </span>
4431
4432
  )
@@ -4465,7 +4466,7 @@ function HPACell({ resource, column }: { resource: any; column: string }) {
4465
4466
  case 'status': {
4466
4467
  const status = getHPAStatus(resource)
4467
4468
  return (
4468
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4469
+ <span className={clsx('badge', status.color)}>
4469
4470
  {status.text}
4470
4471
  </span>
4471
4472
  )
@@ -4566,7 +4567,7 @@ function NodeCell({ resource, column, majorityNodeMinorVersion }: { resource: an
4566
4567
  const { problems } = getNodeConditions(resource)
4567
4568
  return (
4568
4569
  <div className="flex items-center gap-2">
4569
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4570
+ <span className={clsx('badge', status.color)}>
4570
4571
  {status.text}
4571
4572
  </span>
4572
4573
  {problems.length > 0 && (
@@ -4645,7 +4646,7 @@ function PVCCell({ resource, column }: { resource: any; column: string }) {
4645
4646
  case 'status': {
4646
4647
  const status = getPVCStatus(resource)
4647
4648
  return (
4648
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4649
+ <span className={clsx('badge', status.color)}>
4649
4650
  {status.text}
4650
4651
  </span>
4651
4652
  )
@@ -4676,7 +4677,7 @@ function RolloutCell({ resource, column }: { resource: any; column: string }) {
4676
4677
  case 'status': {
4677
4678
  const status = getRolloutStatus(resource)
4678
4679
  return (
4679
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4680
+ <span className={clsx('badge', status.color)}>
4680
4681
  {status.text}
4681
4682
  </span>
4682
4683
  )
@@ -4719,7 +4720,7 @@ function WorkflowCell({ resource, column }: { resource: any; column: string }) {
4719
4720
  case 'status': {
4720
4721
  const status = getWorkflowStatus(resource)
4721
4722
  return (
4722
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4723
+ <span className={clsx('badge', status.color)}>
4723
4724
  {status.text}
4724
4725
  </span>
4725
4726
  )
@@ -4751,7 +4752,7 @@ function PersistentVolumeCell({ resource, column }: { resource: any; column: str
4751
4752
  case 'status': {
4752
4753
  const status = getPVStatus(resource)
4753
4754
  return (
4754
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4755
+ <span className={clsx('badge', status.color)}>
4755
4756
  {status.text}
4756
4757
  </span>
4757
4758
  )
@@ -4823,7 +4824,7 @@ function GatewayCell({ resource, column }: { resource: any; column: string }) {
4823
4824
  case 'status': {
4824
4825
  const status = getGatewayStatus(resource)
4825
4826
  return (
4826
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4827
+ <span className={clsx('badge', status.color)}>
4827
4828
  {status.text}
4828
4829
  </span>
4829
4830
  )
@@ -4858,7 +4859,7 @@ function GatewayClassCell({ resource, column }: { resource: any; column: string
4858
4859
  case 'status': {
4859
4860
  const status = getGatewayClassStatus(resource)
4860
4861
  return (
4861
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4862
+ <span className={clsx('badge', status.color)}>
4862
4863
  {status.text}
4863
4864
  </span>
4864
4865
  )
@@ -4890,7 +4891,7 @@ function RouteCell({ resource, column }: { resource: any; column: string }) {
4890
4891
  case 'status': {
4891
4892
  const status = getRouteStatus(resource)
4892
4893
  return (
4893
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4894
+ <span className={clsx('badge', status.color)}>
4894
4895
  {status.text}
4895
4896
  </span>
4896
4897
  )
@@ -4925,7 +4926,7 @@ function SealedSecretCell({ resource, column }: { resource: any; column: string
4925
4926
  case 'status': {
4926
4927
  const status = getSealedSecretStatus(resource)
4927
4928
  return (
4928
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4929
+ <span className={clsx('badge', status.color)}>
4929
4930
  {status.text}
4930
4931
  </span>
4931
4932
  )
@@ -4976,7 +4977,7 @@ function PDBCell({ resource, column }: { resource: any; column: string }) {
4976
4977
  case 'status': {
4977
4978
  const status = getPDBStatus(resource)
4978
4979
  return (
4979
- <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', status.color)}>
4980
+ <span className={clsx('badge', status.color)}>
4980
4981
  {status.text}
4981
4982
  </span>
4982
4983
  )
@@ -5027,10 +5028,10 @@ function RoleBindingCell({ resource, column }: { resource: any; column: string }
5027
5028
  case 'role': {
5028
5029
  const ref = resource.roleRef
5029
5030
  if (!ref?.name) return <span className="text-sm text-theme-text-tertiary">-</span>
5030
- const kindColor = ref.kind === 'ClusterRole' ? 'bg-purple-500/20 text-purple-400' : 'bg-blue-500/20 text-blue-400'
5031
+ const kindColor = SEVERITY_BADGE.info
5031
5032
  return (
5032
5033
  <span className="text-sm text-theme-text-secondary inline-flex items-center gap-1.5">
5033
- <span className={clsx('px-1.5 py-0.5 rounded text-xs', kindColor)}>{ref.kind === 'ClusterRole' ? 'CR' : 'R'}</span>
5034
+ <span className={clsx('badge-sm', kindColor)}>{ref.kind === 'ClusterRole' ? 'CR' : 'R'}</span>
5034
5035
  {ref.name}
5035
5036
  </span>
5036
5037
  )
@@ -5117,7 +5118,7 @@ function WebhookConfigCell({ resource, column }: { resource: any; column: string
5117
5118
  case 'failurePolicy': {
5118
5119
  const hasFail = webhooks.some((w: any) => w.failurePolicy === 'Fail')
5119
5120
  return hasFail
5120
- ? <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-500/20 text-red-400">Fail</span>
5121
+ ? <span className={clsx('badge', SEVERITY_BADGE.error)}>Fail</span>
5121
5122
  : <span className="text-sm text-theme-text-secondary">Ignore</span>
5122
5123
  }
5123
5124
  case 'target': {
@@ -5141,8 +5142,8 @@ function EventCell({ resource, column }: { resource: any; column: string }) {
5141
5142
  case 'type':
5142
5143
  return (
5143
5144
  <span className={clsx(
5144
- 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium',
5145
- isWarning ? 'bg-amber-500/20 text-amber-400' : 'bg-blue-500/20 text-blue-400'
5145
+ 'badge',
5146
+ EVENT_TYPE_COLORS[eventType] || SEVERITY_BADGE.info
5146
5147
  )}>
5147
5148
  {eventType}
5148
5149
  </span>
@@ -88,7 +88,7 @@ export function AlertRenderer({ data }: AlertRendererProps) {
88
88
  label="Event Severity"
89
89
  value={
90
90
  <span className={clsx(
91
- 'px-2 py-0.5 rounded text-xs font-medium',
91
+ 'badge',
92
92
  eventSeverity === 'error' ? 'bg-red-500/20 text-red-400' :
93
93
  eventSeverity === 'warning' ? 'bg-yellow-500/20 text-yellow-400' :
94
94
  'bg-blue-500/20 text-blue-400'
@@ -108,7 +108,7 @@ export function AlertRenderer({ data }: AlertRendererProps) {
108
108
  {eventSources.map((source: any, idx: number) => (
109
109
  <div
110
110
  key={idx}
111
- className="p-2 bg-theme-elevated/30 rounded text-sm"
111
+ className="card-inner text-sm"
112
112
  >
113
113
  <div className="flex items-center gap-2">
114
114
  <span className="text-theme-text-tertiary">{source.kind}</span>
@@ -132,7 +132,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
132
132
  value={
133
133
  <span
134
134
  className={clsx(
135
- 'px-2 py-0.5 rounded text-xs font-medium',
135
+ 'badge',
136
136
  syncPolicy.automated ? 'bg-green-500/20 text-green-400' : 'bg-gray-500/20 text-gray-400'
137
137
  )}
138
138
  >
@@ -175,7 +175,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
175
175
  value={
176
176
  <span
177
177
  className={clsx(
178
- 'px-2 py-0.5 rounded text-xs font-medium',
178
+ 'badge',
179
179
  operationState.phase === 'Succeeded'
180
180
  ? 'bg-green-500/20 text-green-400'
181
181
  : operationState.phase === 'Running'
@@ -82,7 +82,7 @@ export function CNPGClusterRenderer({ data, onNavigate }: CNPGClusterRendererPro
82
82
  {instanceNames.map((name: string) => (
83
83
  <span
84
84
  key={name}
85
- className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary font-mono"
85
+ className="badge-sm bg-theme-hover text-theme-text-secondary font-mono"
86
86
  >
87
87
  {name}
88
88
  </span>
@@ -103,7 +103,7 @@ export function CertificateRenderer({ data }: CertificateRendererProps) {
103
103
  label="Status"
104
104
  value={
105
105
  <span className={clsx(
106
- 'px-2 py-0.5 rounded text-xs font-medium',
106
+ 'badge',
107
107
  isReady
108
108
  ? 'bg-green-500/20 text-green-400'
109
109
  : 'bg-red-500/20 text-red-400'
@@ -168,7 +168,7 @@ export function CertificateRenderer({ data }: CertificateRendererProps) {
168
168
  {dnsNames.map((name: string) => (
169
169
  <span
170
170
  key={name}
171
- className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary"
171
+ className="badge bg-theme-elevated text-theme-text-secondary"
172
172
  >
173
173
  {name}
174
174
  </span>
@@ -193,7 +193,7 @@ export function CertificateRenderer({ data }: CertificateRendererProps) {
193
193
  {usages.map((usage: string) => (
194
194
  <span
195
195
  key={usage}
196
- className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary"
196
+ className="badge bg-theme-elevated text-theme-text-secondary"
197
197
  >
198
198
  {usage}
199
199
  </span>
@@ -51,7 +51,7 @@ export function CertificateRequestRenderer({ data }: CertificateRequestRendererP
51
51
  label="Ready"
52
52
  value={
53
53
  <span className={clsx(
54
- 'px-2 py-0.5 rounded text-xs font-medium',
54
+ 'badge',
55
55
  isReady
56
56
  ? 'bg-green-500/20 text-green-400'
57
57
  : 'bg-red-500/20 text-red-400'
@@ -64,7 +64,7 @@ export function CertificateRequestRenderer({ data }: CertificateRequestRendererP
64
64
  label="Approved"
65
65
  value={
66
66
  <span className={clsx(
67
- 'px-2 py-0.5 rounded text-xs font-medium',
67
+ 'badge',
68
68
  isApproved
69
69
  ? 'bg-green-500/20 text-green-400'
70
70
  : isDenied
@@ -103,7 +103,7 @@ export function CertificateRequestRenderer({ data }: CertificateRequestRendererP
103
103
  {usages.map((usage: string) => (
104
104
  <span
105
105
  key={usage}
106
- className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary"
106
+ className="badge bg-theme-elevated text-theme-text-secondary"
107
107
  >
108
108
  {usage}
109
109
  </span>
@@ -63,7 +63,7 @@ export function ChallengeRenderer({ data }: { data: any }) {
63
63
  <Property
64
64
  label="State"
65
65
  value={
66
- <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', getChallengeStateBadge(state).color)}>
66
+ <span className={clsx('badge', getChallengeStateBadge(state).color)}>
67
67
  {getChallengeStateBadge(state).text}
68
68
  </span>
69
69
  }
@@ -71,7 +71,7 @@ export function ChallengeRenderer({ data }: { data: any }) {
71
71
  <Property
72
72
  label="Type"
73
73
  value={
74
- <span className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary font-medium">
74
+ <span className="badge bg-theme-elevated text-theme-text-secondary">
75
75
  {challengeType}
76
76
  </span>
77
77
  }
@@ -59,7 +59,7 @@ export function ClusterExternalSecretRenderer({ data }: ClusterExternalSecretRen
59
59
  <div className="text-xs text-theme-text-secondary mb-1.5">Explicit Namespaces</div>
60
60
  <div className="flex flex-wrap gap-1">
61
61
  {explicitNamespaces.map((ns: string) => (
62
- <span key={ns} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-primary">
62
+ <span key={ns} className="badge bg-theme-elevated text-theme-text-primary">
63
63
  {ns}
64
64
  </span>
65
65
  ))}
@@ -70,7 +70,7 @@ export function ClusterExternalSecretRenderer({ data }: ClusterExternalSecretRen
70
70
  <div className="text-xs text-theme-text-secondary mb-1.5">Label Selector</div>
71
71
  <div className="flex flex-wrap gap-1">
72
72
  {Object.entries(namespaceSelector).map(([k, v]) => (
73
- <span key={k} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary">
73
+ <span key={k} className="badge bg-theme-elevated text-theme-text-secondary">
74
74
  {k}={v}
75
75
  </span>
76
76
  ))}
@@ -86,7 +86,7 @@ export function ClusterExternalSecretRenderer({ data }: ClusterExternalSecretRen
86
86
  <Section title={`Provisioned Namespaces (${provisionedNamespaces.length})`} defaultExpanded={provisionedNamespaces.length <= 10}>
87
87
  <div className="flex flex-wrap gap-1">
88
88
  {provisionedNamespaces.map((ns: string) => (
89
- <span key={ns} className="px-2 py-0.5 bg-green-500/10 text-green-400 rounded text-xs">
89
+ <span key={ns} className="badge bg-green-500/10 text-green-400">
90
90
  {ns}
91
91
  </span>
92
92
  ))}
@@ -70,7 +70,7 @@ function IssuerRendererBase({ data, kind }: { data: any; kind: string }) {
70
70
  label="Ready"
71
71
  value={
72
72
  <span className={clsx(
73
- 'px-2 py-0.5 rounded text-xs font-medium',
73
+ 'badge',
74
74
  isReady
75
75
  ? 'bg-green-500/20 text-green-400'
76
76
  : 'bg-red-500/20 text-red-400'
@@ -103,9 +103,9 @@ function IssuerRendererBase({ data, kind }: { data: any; kind: string }) {
103
103
  {solvers.map((solver: any, i: number) => {
104
104
  const { type, detail } = getSolverType(solver)
105
105
  return (
106
- <div key={i} className="bg-theme-elevated/30 rounded p-3">
106
+ <div key={i} className="card-inner-lg">
107
107
  <div className="flex items-center gap-2">
108
- <span className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary font-medium">
108
+ <span className="badge bg-theme-elevated text-theme-text-secondary">
109
109
  {type}
110
110
  </span>
111
111
  {detail && (
@@ -134,12 +134,12 @@ export function ConfigAuditReportRenderer({ data }: ConfigAuditReportRendererPro
134
134
  { label: 'Medium', count: medium, key: 'MEDIUM' },
135
135
  { label: 'Low', count: low, key: 'LOW' },
136
136
  ].filter(({ count }) => count > 0).map(({ label, count, key }) => (
137
- <span key={key} className={clsx('px-2 py-0.5 rounded text-xs font-medium', SEVERITY_BADGE_COLORS[key])}>
137
+ <span key={key} className={clsx('badge', SEVERITY_BADGE_COLORS[key])}>
138
138
  {count} {label}
139
139
  </span>
140
140
  ))}
141
141
  {failCount === 0 && (
142
- <span className="px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400">All checks passed</span>
142
+ <span className="badge status-healthy">All checks passed</span>
143
143
  )}
144
144
  </div>
145
145
  </Section>
@@ -61,7 +61,7 @@ export function ContourHTTPProxyRenderer({ data, onNavigate }: ContourHTTPProxyR
61
61
  const routeConditions = route.conditions || []
62
62
 
63
63
  return (
64
- <div key={i} className="bg-theme-elevated/30 rounded p-3">
64
+ <div key={i} className="card-inner-lg">
65
65
  {/* Conditions (prefix match, header match, etc.) */}
66
66
  {routeConditions.length > 0 && (
67
67
  <div className="flex items-start gap-2 mb-2">
@@ -39,7 +39,7 @@ export function EventRenderer({ data, onNavigate }: EventRendererProps) {
39
39
  <div className="flex-1 min-w-0">
40
40
  <div className="flex items-center gap-2 mb-1">
41
41
  <span className={clsx(
42
- 'px-2 py-0.5 rounded text-xs font-medium',
42
+ 'badge',
43
43
  isWarning ? 'bg-amber-500/20 text-amber-400' : 'bg-blue-500/20 text-blue-400'
44
44
  )}>
45
45
  {eventType}
@@ -54,12 +54,12 @@ export function ExposedSecretReportRenderer({ data }: ExposedSecretReportRendere
54
54
  { label: 'Medium', count: medium, key: 'MEDIUM' },
55
55
  { label: 'Low', count: low, key: 'LOW' },
56
56
  ].filter(({ count }) => count > 0).map(({ label, count, key }) => (
57
- <span key={key} className={clsx('px-2 py-0.5 rounded text-xs font-medium', SEVERITY_BADGE_COLORS[key])}>
57
+ <span key={key} className={clsx('badge', SEVERITY_BADGE_COLORS[key])}>
58
58
  {count} {label}
59
59
  </span>
60
60
  ))}
61
61
  {secrets.length === 0 && (
62
- <span className="px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400">No secrets found</span>
62
+ <span className="badge status-healthy">No secrets found</span>
63
63
  )}
64
64
  </div>
65
65
  </Section>
@@ -130,9 +130,9 @@ export function ExternalSecretRenderer({ data, onNavigate }: ExternalSecretRende
130
130
  <Section title={`Data Sources (${dataFromSources.length})`} defaultExpanded>
131
131
  <div className="space-y-1">
132
132
  {dataFromSources.map((source, i) => (
133
- <div key={i} className="bg-theme-elevated/30 rounded p-2">
133
+ <div key={i} className="card-inner">
134
134
  <div className="flex items-center gap-2 text-sm">
135
- <span className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary font-medium">
135
+ <span className="badge-sm bg-theme-hover text-theme-text-secondary">
136
136
  {source.type}
137
137
  </span>
138
138
  {source.details && (