@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.
- package/package.json +3 -2
- package/src/components/dock/BottomDock.tsx +1 -1
- package/src/components/dock/LocalTerminalTab.tsx +1 -1
- package/src/components/dock/NodeTerminalTab.tsx +1 -1
- package/src/components/dock/TerminalTab.tsx +2 -2
- package/src/components/gitops/GitOpsActions.tsx +12 -11
- package/src/components/gitops/GitOpsStatusBadge.tsx +6 -6
- package/src/components/logs/LogCore.tsx +8 -8
- package/src/components/logs/WorkloadLogsViewer.tsx +29 -5
- package/src/components/logs/useLogStream.ts +20 -0
- package/src/components/resources/ResourcesSidebar.tsx +10 -10
- package/src/components/resources/ResourcesView.tsx +49 -48
- package/src/components/resources/renderers/AlertRenderer.tsx +2 -2
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +2 -2
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -1
- package/src/components/resources/renderers/CertificateRenderer.tsx +3 -3
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +3 -3
- package/src/components/resources/renderers/ChallengeRenderer.tsx +2 -2
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +3 -3
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +3 -3
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +1 -1
- package/src/components/resources/renderers/EventRenderer.tsx +1 -1
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +2 -2
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -1
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +5 -5
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -1
- package/src/components/resources/renderers/GatewayRenderer.tsx +3 -3
- package/src/components/resources/renderers/GenericRenderer.tsx +1 -1
- package/src/components/resources/renderers/HPARenderer.tsx +1 -1
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +5 -5
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -1
- package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -1
- package/src/components/resources/renderers/IngressRenderer.tsx +1 -1
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +10 -10
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -1
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +2 -2
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +2 -2
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +6 -6
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -5
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +5 -5
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -2
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +4 -4
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +3 -3
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +3 -3
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +5 -5
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +2 -2
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +5 -5
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +4 -4
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +8 -8
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +3 -3
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -1
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +2 -2
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +6 -6
- package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -1
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +4 -4
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -6
- package/src/components/resources/renderers/NodeRenderer.tsx +5 -5
- package/src/components/resources/renderers/OrderRenderer.tsx +3 -3
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -1
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -1
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +3 -3
- package/src/components/resources/renderers/PodRenderer.tsx +10 -10
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +2 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +3 -3
- package/src/components/resources/renderers/RoleRenderer.tsx +8 -8
- package/src/components/resources/renderers/RolloutRenderer.tsx +1 -1
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +2 -2
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +2 -2
- package/src/components/resources/renderers/SecretRenderer.tsx +3 -3
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -1
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +3 -3
- package/src/components/resources/renderers/ServiceRenderer.tsx +1 -1
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +5 -5
- package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -1
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -1
- package/src/components/resources/renderers/VPARenderer.tsx +2 -2
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +2 -2
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +6 -6
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +5 -5
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +6 -6
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -1
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +10 -10
- package/src/components/resources/renderers/WorkflowRenderer.tsx +2 -2
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +2 -2
- package/src/components/resources/renderers/WorkloadRenderer.tsx +1 -1
- package/src/components/resources/renderers/argo-cells.tsx +3 -3
- package/src/components/resources/renderers/certmanager-cells.tsx +6 -6
- package/src/components/resources/renderers/cnpg-cells.tsx +5 -5
- package/src/components/resources/renderers/eso-cells.tsx +4 -4
- package/src/components/resources/renderers/flux-cells.tsx +6 -6
- package/src/components/resources/renderers/istio-cells.tsx +9 -9
- package/src/components/resources/renderers/karpenter-cells.tsx +3 -3
- package/src/components/resources/renderers/keda-cells.tsx +2 -2
- package/src/components/resources/renderers/knative-cells.tsx +2 -2
- package/src/components/resources/renderers/kyverno-cells.tsx +3 -3
- package/src/components/resources/renderers/prometheus-cells.tsx +3 -3
- package/src/components/resources/renderers/trivy-cells.tsx +4 -4
- package/src/components/resources/renderers/velero-cells.tsx +4 -4
- package/src/components/shared/EditableYamlView.tsx +1 -1
- package/src/components/shared/ResourceActionsBar.tsx +17 -17
- package/src/components/shared/ResourceRendererDispatch.tsx +8 -7
- package/src/components/topology/K8sResourceNode.tsx +5 -5
- package/src/components/topology/TopologyControls.tsx +2 -2
- package/src/components/topology/TopologyGraph.tsx +89 -23
- package/src/components/topology/topology.css +1 -1
- package/src/components/ui/Badge.tsx +178 -0
- package/src/components/ui/HealthRing.tsx +70 -35
- package/src/components/ui/MetricsChart.tsx +2 -2
- package/src/components/ui/drawer-components.tsx +12 -31
- package/src/components/workload/ResourceDetailDrawer.tsx +4 -4
- package/src/components/workload/WorkloadView.tsx +10 -10
- package/src/theme/components.css +195 -0
- package/src/theme/tailwind-theme.css +13 -13
- package/src/theme/variables.css +46 -29
- package/src/types/core.ts +2 -0
- package/src/utils/badge-colors.ts +54 -205
- package/src/utils/log-format.ts +0 -8
|
@@ -111,7 +111,7 @@ export function MetricsChart({
|
|
|
111
111
|
{/* Chart with Y-axis */}
|
|
112
112
|
<div className="flex">
|
|
113
113
|
{/* Y-axis labels */}
|
|
114
|
-
<div className="flex flex-col justify-between text-[9px] text-theme-text-tertiary pr-1 w-12 text-right" style={{ height }}>
|
|
114
|
+
<div className="flex flex-col justify-between text-[9px] text-theme-text-tertiary pr-1 w-12 text-right font-mono" style={{ height }}>
|
|
115
115
|
<span className="leading-none">{format(yAxisMax)}</span>
|
|
116
116
|
<span className="leading-none">{format(yAxisMid)}</span>
|
|
117
117
|
<span className="leading-none">0</span>
|
|
@@ -197,7 +197,7 @@ export function MetricsChart({
|
|
|
197
197
|
)}
|
|
198
198
|
|
|
199
199
|
{/* Current value */}
|
|
200
|
-
<div className="flex items-baseline gap-2 mt-1 ml-12">
|
|
200
|
+
<div className="flex items-baseline gap-2 mt-1 ml-12 font-mono">
|
|
201
201
|
<span className={clsx(
|
|
202
202
|
'text-sm font-medium',
|
|
203
203
|
type === 'cpu' ? 'text-blue-400' : 'text-purple-400'
|
|
@@ -3,6 +3,7 @@ import { ChevronRight, Copy, Check, Tag, AlertTriangle, CheckCircle, ExternalLin
|
|
|
3
3
|
import { clsx } from 'clsx'
|
|
4
4
|
import { formatAge } from '../resources/resource-utils'
|
|
5
5
|
import { Tooltip } from './Tooltip'
|
|
6
|
+
import { getKindColorClass } from '../ui/Badge'
|
|
6
7
|
|
|
7
8
|
// ============================================================================
|
|
8
9
|
// UI COMPONENTS
|
|
@@ -10,7 +11,7 @@ import { Tooltip } from './Tooltip'
|
|
|
10
11
|
|
|
11
12
|
export function KeyValueBadge({ k, v }: { k: string; v: string }) {
|
|
12
13
|
return (
|
|
13
|
-
<span className="
|
|
14
|
+
<span className="badge bg-theme-elevated text-theme-text-secondary">
|
|
14
15
|
{k}={v}
|
|
15
16
|
</span>
|
|
16
17
|
)
|
|
@@ -164,10 +165,10 @@ export function ConditionsSection({ conditions }: { conditions?: any[] }) {
|
|
|
164
165
|
// ============================================================================
|
|
165
166
|
|
|
166
167
|
const ALERT_COLORS = {
|
|
167
|
-
error: { bg: 'bg-red-
|
|
168
|
-
warning: { bg: 'bg-
|
|
169
|
-
info: { bg: 'bg-
|
|
170
|
-
success: { bg: 'bg-
|
|
168
|
+
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' },
|
|
169
|
+
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' },
|
|
170
|
+
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' },
|
|
171
|
+
success: { bg: 'bg-emerald-50 dark:bg-emerald-950/30', border: 'border-emerald-200 dark:border-emerald-800/30', title: 'text-emerald-700 dark:text-emerald-300', message: 'text-emerald-600/80 dark:text-emerald-300/70', list: 'text-emerald-600 dark:text-emerald-400', bullet: 'text-emerald-400/60 dark:text-emerald-400/50' },
|
|
171
172
|
} as const
|
|
172
173
|
|
|
173
174
|
const DEFAULT_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
|
|
@@ -313,7 +314,7 @@ export function PodTemplateSection({ template }: { template: any }) {
|
|
|
313
314
|
<>
|
|
314
315
|
<div className="text-xs text-theme-text-tertiary font-medium uppercase tracking-wide">Init Containers</div>
|
|
315
316
|
{initContainers.map((c: any) => (
|
|
316
|
-
<div key={c.name} className="
|
|
317
|
+
<div key={c.name} className="card-inner text-sm border-l-2 border-yellow-500/40">
|
|
317
318
|
<div className="font-medium text-theme-text-primary">{c.name}</div>
|
|
318
319
|
<div className="text-xs text-theme-text-secondary truncate" title={c.image}>{c.image}</div>
|
|
319
320
|
{(c.command || c.args) && (
|
|
@@ -327,7 +328,7 @@ export function PodTemplateSection({ template }: { template: any }) {
|
|
|
327
328
|
</>
|
|
328
329
|
)}
|
|
329
330
|
{containers.map((c: any) => (
|
|
330
|
-
<div key={c.name} className="
|
|
331
|
+
<div key={c.name} className="card-inner text-sm">
|
|
331
332
|
<div className="font-medium text-theme-text-primary">{c.name}</div>
|
|
332
333
|
<div className="text-xs text-theme-text-secondary truncate" title={c.image}>{c.image}</div>
|
|
333
334
|
{c.ports && (
|
|
@@ -450,29 +451,9 @@ export function AppInfoSection({ data }: { data: any }) {
|
|
|
450
451
|
// HELPERS
|
|
451
452
|
// ============================================================================
|
|
452
453
|
|
|
454
|
+
/** Get kind badge color — delegates to Badge.tsx's static color lookup */
|
|
453
455
|
export function getKindColor(kind: string): string {
|
|
454
|
-
|
|
455
|
-
// Use darker text for light mode contrast, brighter for dark mode
|
|
456
|
-
if (k.includes('pod')) return 'bg-lime-500/20 text-lime-700 dark:text-lime-400 border-lime-500/30'
|
|
457
|
-
if (k.includes('deployment')) return 'bg-emerald-500/20 text-emerald-700 dark:text-emerald-400 border-emerald-500/30'
|
|
458
|
-
if (k.includes('service')) return 'bg-blue-500/20 text-blue-700 dark:text-blue-400 border-blue-500/30'
|
|
459
|
-
if (k.includes('gateway')) return 'bg-violet-500/20 text-violet-700 dark:text-violet-400 border-violet-500/30'
|
|
460
|
-
if (k.includes('route')) return 'bg-purple-500/20 text-purple-700 dark:text-purple-400 border-purple-500/30'
|
|
461
|
-
if (k.includes('ingress')) return 'bg-violet-500/20 text-violet-700 dark:text-violet-400 border-violet-500/30'
|
|
462
|
-
if (k.includes('configmap')) return 'bg-amber-500/20 text-amber-700 dark:text-amber-400 border-amber-500/30'
|
|
463
|
-
if (k.endsWith('report')) return 'bg-rose-500/20 text-rose-700 dark:text-rose-400 border-rose-500/30'
|
|
464
|
-
if (k.includes('secret')) return 'bg-red-500/20 text-red-700 dark:text-red-400 border-red-500/30'
|
|
465
|
-
if (k.includes('daemonset')) return 'bg-teal-500/20 text-teal-700 dark:text-teal-400 border-teal-500/30'
|
|
466
|
-
if (k.includes('statefulset')) return 'bg-cyan-500/20 text-cyan-700 dark:text-cyan-400 border-cyan-500/30'
|
|
467
|
-
if (k.includes('replicaset')) return 'bg-green-500/20 text-green-700 dark:text-green-400 border-green-500/30'
|
|
468
|
-
if (k.includes('hpa') || k.includes('horizontalpodautoscaler')) return 'bg-pink-500/20 text-pink-700 dark:text-pink-400 border-pink-500/30'
|
|
469
|
-
if (k.includes('cronjob')) return 'bg-orange-500/20 text-orange-700 dark:text-orange-400 border-orange-500/30'
|
|
470
|
-
if (k.includes('job')) return 'bg-orange-500/20 text-orange-700 dark:text-orange-400 border-orange-500/30'
|
|
471
|
-
if (k.includes('node')) return 'bg-gray-500/20 text-gray-700 dark:text-gray-400 border-gray-500/30'
|
|
472
|
-
if (k.includes('namespace')) return 'bg-blue-500/20 text-blue-700 dark:text-blue-400 border-blue-500/30'
|
|
473
|
-
if (k.includes('persistentvolume')) return 'bg-purple-500/20 text-purple-700 dark:text-purple-400 border-purple-500/30'
|
|
474
|
-
// Default color for CRDs
|
|
475
|
-
return 'bg-fuchsia-500/20 text-fuchsia-700 dark:text-fuchsia-300 border-fuchsia-500/30'
|
|
456
|
+
return getKindColorClass(kind)
|
|
476
457
|
}
|
|
477
458
|
|
|
478
459
|
export function formatKindName(kind: string): string {
|
|
@@ -648,7 +629,7 @@ export function ResourceRefBadge({ resourceRef, onClick }: ResourceRefBadgeProps
|
|
|
648
629
|
<button
|
|
649
630
|
onClick={() => onClick(resourceRef)}
|
|
650
631
|
className={clsx(
|
|
651
|
-
'
|
|
632
|
+
'badge hover:brightness-[0.92] dark:hover:brightness-125 transition-[filter]',
|
|
652
633
|
kindClass
|
|
653
634
|
)}
|
|
654
635
|
title={`${resourceRef.kind}: ${resourceRef.namespace}/${resourceRef.name}`}
|
|
@@ -661,7 +642,7 @@ export function ResourceRefBadge({ resourceRef, onClick }: ResourceRefBadgeProps
|
|
|
661
642
|
|
|
662
643
|
return (
|
|
663
644
|
<span
|
|
664
|
-
className={clsx('
|
|
645
|
+
className={clsx('badge', kindClass)}
|
|
665
646
|
title={`${resourceRef.kind}: ${resourceRef.namespace}/${resourceRef.name}`}
|
|
666
647
|
>
|
|
667
648
|
<span className="opacity-60">{kindName}/</span>
|
|
@@ -115,12 +115,12 @@ export function ResourceDetailDrawer({ resource, onClose, onNavigate, initialTab
|
|
|
115
115
|
return (
|
|
116
116
|
<div
|
|
117
117
|
className={clsx(
|
|
118
|
-
'fixed right-0 bg-theme-surface border-l border-theme-border flex flex-col shadow-
|
|
118
|
+
'fixed right-0 bg-theme-surface border-l border-theme-border flex flex-col shadow-drawer z-40',
|
|
119
119
|
TRANSITION_DRAWER,
|
|
120
120
|
isOpen
|
|
121
121
|
? 'translate-x-0 opacity-100'
|
|
122
122
|
: 'translate-x-full opacity-0',
|
|
123
|
-
expanded && '
|
|
123
|
+
expanded && '!border-l-0',
|
|
124
124
|
)}
|
|
125
125
|
style={{
|
|
126
126
|
width: expanded ? `calc(100vw - ${leftOffset}px)` : drawerWidth,
|
|
@@ -136,9 +136,9 @@ export function ResourceDetailDrawer({ resource, onClose, onNavigate, initialTab
|
|
|
136
136
|
<div
|
|
137
137
|
onMouseDown={handleResizeStart}
|
|
138
138
|
className={clsx(
|
|
139
|
-
'absolute left-0 top-0 bottom-0 w-2 cursor-ew-resize z-10 hover:bg-
|
|
139
|
+
'absolute left-0 top-0 bottom-0 w-2 cursor-ew-resize z-10 hover:bg-skyhook-500/50 transition-colors',
|
|
140
140
|
'hidden sm:block',
|
|
141
|
-
isResizing && 'bg-
|
|
141
|
+
isResizing && 'bg-skyhook-500/50'
|
|
142
142
|
)}
|
|
143
143
|
/>
|
|
144
144
|
)}
|
|
@@ -351,11 +351,11 @@ export function WorkloadView({
|
|
|
351
351
|
{/* Top row: badges and controls */}
|
|
352
352
|
<div className="flex items-center justify-between px-4 pt-3 pb-2">
|
|
353
353
|
<div className="flex items-center gap-2 flex-wrap">
|
|
354
|
-
<span className={clsx('
|
|
354
|
+
<span className={clsx('badge', getKindColor(apiKind))}>
|
|
355
355
|
{formatKindName(apiKind)}
|
|
356
356
|
</span>
|
|
357
357
|
{status && (
|
|
358
|
-
<span className={clsx('
|
|
358
|
+
<span className={clsx('badge', status.color)}>
|
|
359
359
|
{status.text}
|
|
360
360
|
</span>
|
|
361
361
|
)}
|
|
@@ -454,7 +454,7 @@ export function WorkloadView({
|
|
|
454
454
|
|
|
455
455
|
// ── Expanded (full) mode ─────────────────────────────────────────────────
|
|
456
456
|
return (
|
|
457
|
-
<div className="flex flex-col h-full w-full bg-theme-
|
|
457
|
+
<div className="flex flex-col h-full w-full bg-theme-surface">
|
|
458
458
|
{/* Header */}
|
|
459
459
|
<div className="shrink-0 border-b border-theme-border bg-theme-surface">
|
|
460
460
|
<div className="px-6 py-3 flex items-start gap-4">
|
|
@@ -480,11 +480,11 @@ export function WorkloadView({
|
|
|
480
480
|
</button>
|
|
481
481
|
</div>
|
|
482
482
|
<div className="flex items-center gap-3 text-sm text-theme-text-secondary">
|
|
483
|
-
<span className={clsx('
|
|
483
|
+
<span className={clsx('badge', getKindColor(apiKind))}>
|
|
484
484
|
{formatKindName(apiKind)}
|
|
485
485
|
</span>
|
|
486
486
|
{status && (
|
|
487
|
-
<span className={clsx('
|
|
487
|
+
<span className={clsx('badge', status.color)}>
|
|
488
488
|
{status.text}
|
|
489
489
|
</span>
|
|
490
490
|
)}
|
|
@@ -539,7 +539,7 @@ export function WorkloadView({
|
|
|
539
539
|
<Activity className="w-4 h-4" />
|
|
540
540
|
Timeline
|
|
541
541
|
{resourceEvents.length > 0 && (
|
|
542
|
-
<span className="ml-1
|
|
542
|
+
<span className="ml-1 badge-sm bg-theme-elevated">{resourceEvents.length}</span>
|
|
543
543
|
)}
|
|
544
544
|
</TabButton>
|
|
545
545
|
{allPods.length > 0 && renderLogsTab && (
|
|
@@ -692,7 +692,7 @@ function TabButton({ active, onClick, children }: { active: boolean; onClick: ()
|
|
|
692
692
|
className={clsx(
|
|
693
693
|
'flex items-center gap-1.5 px-3 py-2 text-sm font-medium border-b-2 transition-colors',
|
|
694
694
|
active
|
|
695
|
-
? 'text-theme-text-primary border-
|
|
695
|
+
? 'text-theme-text-primary border-skyhook-500'
|
|
696
696
|
: 'text-theme-text-secondary border-transparent hover:text-theme-text-primary hover:border-theme-border-light'
|
|
697
697
|
)}
|
|
698
698
|
>
|
|
@@ -1016,15 +1016,15 @@ function EventsTab({
|
|
|
1016
1016
|
{new Date(evt.timestamp).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
|
|
1017
1017
|
</td>
|
|
1018
1018
|
<td className="px-4 py-3">
|
|
1019
|
-
<span className={clsx('
|
|
1019
|
+
<span className={clsx('badge-sm', getKindBadgeColor(evt.kind))}>
|
|
1020
1020
|
{evt.kind}
|
|
1021
1021
|
</span>
|
|
1022
1022
|
</td>
|
|
1023
1023
|
<td className="px-4 py-3">
|
|
1024
1024
|
{isWarning ? (
|
|
1025
|
-
<span className="
|
|
1025
|
+
<span className="badge status-degraded">Active</span>
|
|
1026
1026
|
) : evt.healthState ? (
|
|
1027
|
-
<span className={clsx('
|
|
1027
|
+
<span className={clsx('badge', getHealthBadgeColor(evt.healthState))}>{evt.healthState}</span>
|
|
1028
1028
|
) : null}
|
|
1029
1029
|
</td>
|
|
1030
1030
|
</tr>
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
SHARED COMPONENT CLASSES (@skyhook-io/k8s-ui)
|
|
3
|
+
All in @layer components so Tailwind utilities can override them.
|
|
4
|
+
Import: @skyhook-io/k8s-ui/theme/components.css
|
|
5
|
+
Requires: @skyhook-io/k8s-ui/theme/variables.css imported first.
|
|
6
|
+
============================================ */
|
|
7
|
+
|
|
8
|
+
@layer components {
|
|
9
|
+
|
|
10
|
+
/* ── BRAND BUTTONS ── */
|
|
11
|
+
|
|
12
|
+
.btn-brand {
|
|
13
|
+
background-color: var(--color-brand);
|
|
14
|
+
color: white;
|
|
15
|
+
border-radius: 0.625rem;
|
|
16
|
+
transition: background-color 0.15s ease;
|
|
17
|
+
}
|
|
18
|
+
.btn-brand:hover {
|
|
19
|
+
background-color: var(--color-brand-light);
|
|
20
|
+
}
|
|
21
|
+
.btn-brand:disabled {
|
|
22
|
+
opacity: 0.5;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.btn-brand-muted {
|
|
26
|
+
background-color: light-dark(var(--color-brand-light), color-mix(in srgb, var(--color-brand) 60%, black));
|
|
27
|
+
color: white;
|
|
28
|
+
border-radius: 0.625rem;
|
|
29
|
+
transition: background-color 0.15s ease;
|
|
30
|
+
}
|
|
31
|
+
.btn-brand-muted:hover {
|
|
32
|
+
background-color: var(--color-brand);
|
|
33
|
+
}
|
|
34
|
+
.btn-brand-muted:disabled {
|
|
35
|
+
opacity: 0.5;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.btn-brand-toggle {
|
|
39
|
+
background-color: color-mix(in srgb, var(--color-brand) 50%, transparent);
|
|
40
|
+
color: var(--text-primary);
|
|
41
|
+
border-radius: 0.375rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ── SELECTION / HIGHLIGHT ── */
|
|
45
|
+
|
|
46
|
+
.selection {
|
|
47
|
+
background-color: var(--selection-bg);
|
|
48
|
+
}
|
|
49
|
+
.selection-strong {
|
|
50
|
+
background-color: var(--selection-bg-strong);
|
|
51
|
+
}
|
|
52
|
+
.selection-ring {
|
|
53
|
+
box-shadow: inset 0 0 0 1px var(--selection-border);
|
|
54
|
+
}
|
|
55
|
+
.selection-text {
|
|
56
|
+
color: var(--selection-text);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ── DIALOG / MODAL ── */
|
|
60
|
+
|
|
61
|
+
.dialog {
|
|
62
|
+
background-color: var(--bg-surface);
|
|
63
|
+
border: 0.5px solid var(--border-default);
|
|
64
|
+
border-radius: 0.875rem;
|
|
65
|
+
box-shadow: var(--shadow-lg);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ── BADGES ── */
|
|
69
|
+
|
|
70
|
+
.badge {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 0.25rem;
|
|
74
|
+
padding: 0.125rem 0.5rem;
|
|
75
|
+
font-size: 0.75rem;
|
|
76
|
+
font-weight: 500;
|
|
77
|
+
border-radius: 0.375rem;
|
|
78
|
+
border-width: 0.5px;
|
|
79
|
+
border-style: solid;
|
|
80
|
+
border-color: transparent;
|
|
81
|
+
line-height: 1.25rem;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.badge-sm {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 0.25rem;
|
|
88
|
+
padding: 0.125rem 0.375rem;
|
|
89
|
+
font-size: 0.75rem;
|
|
90
|
+
border-radius: 0.375rem;
|
|
91
|
+
border-width: 0.5px;
|
|
92
|
+
border-style: solid;
|
|
93
|
+
border-color: transparent;
|
|
94
|
+
line-height: 1.25rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ── INNER CARDS ── */
|
|
98
|
+
|
|
99
|
+
.card-inner {
|
|
100
|
+
background: var(--bg-base);
|
|
101
|
+
border-radius: 0.375rem;
|
|
102
|
+
border: 1px solid var(--border-subtle);
|
|
103
|
+
padding: 0.5rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.card-inner-lg {
|
|
107
|
+
background: var(--bg-base);
|
|
108
|
+
border-radius: 0.625rem;
|
|
109
|
+
border: 1px solid var(--border-subtle);
|
|
110
|
+
padding: 0.75rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* ── SUBTLE BORDERS ── */
|
|
114
|
+
|
|
115
|
+
.table-divide-subtle > * + * {
|
|
116
|
+
border-top-width: 0.5px;
|
|
117
|
+
border-top-style: solid;
|
|
118
|
+
border-top-color: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.07));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.border-b-subtle {
|
|
122
|
+
border-bottom: 0.5px solid light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.07));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.border-r-subtle {
|
|
126
|
+
border-right: 0.5px solid light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.07));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.border-t-subtle {
|
|
130
|
+
border-top: 0.5px solid light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.07));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ── STATUS BADGE COLORS ── */
|
|
134
|
+
|
|
135
|
+
/* Healthy/Success - emerald */
|
|
136
|
+
.status-healthy {
|
|
137
|
+
background-color: light-dark(#d1fae5, rgba(6, 78, 59, 0.5));
|
|
138
|
+
color: light-dark(#047857, #6ee7b7);
|
|
139
|
+
border-color: light-dark(#6ee7b7, rgba(6, 78, 59, 0.4));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Degraded/Warning - amber */
|
|
143
|
+
.status-degraded {
|
|
144
|
+
background-color: light-dark(#fef3c7, rgba(120, 53, 15, 0.5));
|
|
145
|
+
color: light-dark(#92400e, #fcd34d);
|
|
146
|
+
border-color: light-dark(#fcd34d, rgba(120, 53, 15, 0.4));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Unhealthy/Error - red */
|
|
150
|
+
.status-unhealthy {
|
|
151
|
+
background-color: light-dark(#fee2e2, rgba(127, 29, 29, 0.5));
|
|
152
|
+
color: light-dark(#b91c1c, #fca5a5);
|
|
153
|
+
border-color: light-dark(#fca5a5, rgba(127, 29, 29, 0.4));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Neutral/Info - sky */
|
|
157
|
+
.status-neutral {
|
|
158
|
+
background-color: light-dark(#e0f2fe, rgba(12, 74, 110, 0.5));
|
|
159
|
+
color: light-dark(#0369a1, #7dd3fc);
|
|
160
|
+
border-color: light-dark(#7dd3fc, rgba(12, 74, 110, 0.4));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Unknown/Muted - gray */
|
|
164
|
+
.status-unknown {
|
|
165
|
+
background-color: var(--bg-elevated);
|
|
166
|
+
color: var(--text-secondary);
|
|
167
|
+
border-color: var(--border-default);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Special status colors — no border-color (renders borderless within .badge) */
|
|
171
|
+
.status-violet {
|
|
172
|
+
background-color: light-dark(rgba(139, 92, 246, 0.15), rgba(139, 92, 246, 0.2));
|
|
173
|
+
color: light-dark(#7c3aed, #a78bfa);
|
|
174
|
+
border-color: transparent;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.status-purple {
|
|
178
|
+
background-color: light-dark(rgba(168, 85, 247, 0.15), rgba(168, 85, 247, 0.2));
|
|
179
|
+
color: light-dark(#9333ea, #c084fc);
|
|
180
|
+
border-color: transparent;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.status-orange {
|
|
184
|
+
background-color: light-dark(rgba(249, 115, 22, 0.15), rgba(249, 115, 22, 0.2));
|
|
185
|
+
color: light-dark(#ea580c, #fb923c);
|
|
186
|
+
border-color: transparent;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.status-cyan {
|
|
190
|
+
background-color: light-dark(rgba(6, 182, 212, 0.15), rgba(6, 182, 212, 0.2));
|
|
191
|
+
color: light-dark(#0891b2, #22d3ee);
|
|
192
|
+
border-color: transparent;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
} /* end @layer components */
|
|
@@ -31,21 +31,21 @@
|
|
|
31
31
|
--color-accent-muted: var(--accent-muted);
|
|
32
32
|
--color-accent-text: var(--accent-text);
|
|
33
33
|
|
|
34
|
-
/* Skyhook brand */
|
|
35
|
-
--color-skyhook: #
|
|
36
|
-
--color-skyhook-50: #
|
|
37
|
-
--color-skyhook-100: #
|
|
38
|
-
--color-skyhook-200: #
|
|
39
|
-
--color-skyhook-300: #
|
|
40
|
-
--color-skyhook-400: #
|
|
41
|
-
--color-skyhook-500: #
|
|
42
|
-
--color-skyhook-600: #
|
|
43
|
-
--color-skyhook-700: #
|
|
44
|
-
--color-skyhook-800: #
|
|
45
|
-
--color-skyhook-900: #
|
|
34
|
+
/* Skyhook brand (refined blue palette — more saturated) */
|
|
35
|
+
--color-skyhook: #5088CC;
|
|
36
|
+
--color-skyhook-50: #EBF2FA;
|
|
37
|
+
--color-skyhook-100: #D2E2F4;
|
|
38
|
+
--color-skyhook-200: #A8C7E8;
|
|
39
|
+
--color-skyhook-300: #7EACDC;
|
|
40
|
+
--color-skyhook-400: #6A9FE0;
|
|
41
|
+
--color-skyhook-500: #5088CC;
|
|
42
|
+
--color-skyhook-600: #4A7CC9;
|
|
43
|
+
--color-skyhook-700: #3B66A8;
|
|
44
|
+
--color-skyhook-800: #2D4E82;
|
|
45
|
+
--color-skyhook-900: #1F365C;
|
|
46
46
|
|
|
47
47
|
/* K8s resource type colors */
|
|
48
|
-
--color-k8s-internet: #
|
|
48
|
+
--color-k8s-internet: #5088CC;
|
|
49
49
|
--color-k8s-ingress: #8b5cf6;
|
|
50
50
|
--color-k8s-service: #3b82f6;
|
|
51
51
|
--color-k8s-deployment: #10b981;
|
package/src/theme/variables.css
CHANGED
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
:root {
|
|
14
14
|
color-scheme: light dark;
|
|
15
15
|
|
|
16
|
-
/* ── Brand ── */
|
|
17
|
-
--color-brand: #
|
|
18
|
-
--color-brand-light: light-dark(#
|
|
16
|
+
/* ── Brand (refined blue — more life than slate, less generic than tech blue) ── */
|
|
17
|
+
--color-brand: light-dark(#4A7CC9, #6A9FE0);
|
|
18
|
+
--color-brand-light: light-dark(#3B66A8, #8BB5EA);
|
|
19
|
+
--brand-rgb: light-dark(74 124 201, 106 159 224);
|
|
19
20
|
|
|
20
21
|
/* ── Semantic status (theme-independent) ── */
|
|
21
22
|
--color-success: #22C55E;
|
|
@@ -29,40 +30,55 @@
|
|
|
29
30
|
--color-error-dark: #DC2626;
|
|
30
31
|
--color-info: #3B82F6;
|
|
31
32
|
|
|
32
|
-
/* ── Backgrounds ──
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
--bg-
|
|
37
|
-
--bg-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
--
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
--text-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
--
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
/* ── Backgrounds ──
|
|
34
|
+
base = page bg, header/toolbar chrome (barely-blue cool gray)
|
|
35
|
+
surface = sidebars, content panels, cards (near-white)
|
|
36
|
+
elevated = inputs, count badges, dropdowns (very lightly blue-tinted) */
|
|
37
|
+
--bg-base: light-dark(#F0F3F9, #0B0F1E);
|
|
38
|
+
--bg-surface: light-dark(#F8FBFE, #14192A);
|
|
39
|
+
--bg-elevated: light-dark(#E6ECF5, #1E2338);
|
|
40
|
+
--bg-hover: light-dark(#DDE5F2, #282E48);
|
|
41
|
+
--bg-active: light-dark(#D3DCEC, #1C2840);
|
|
42
|
+
|
|
43
|
+
/* ── Text (cool neutral) ── */
|
|
44
|
+
--text-primary: light-dark(#1A1C20, #F5F6F8);
|
|
45
|
+
--text-secondary: light-dark(#555860, #D8DAE0);
|
|
46
|
+
--text-tertiary: light-dark(#7E828A, #B0B5C0);
|
|
47
|
+
--text-disabled: light-dark(#A0A4AC, #6A7080);
|
|
48
|
+
|
|
49
|
+
/* ── Borders (lightly blue-tinted in light mode) ── */
|
|
50
|
+
--border-default: light-dark(#D8E0EE, #222840);
|
|
51
|
+
--border-light: light-dark(#CDD6E8, #2C324A);
|
|
52
|
+
--border-subtle: light-dark(rgba(74, 100, 180, 0.07), rgba(255, 255, 255, 0.04));
|
|
49
53
|
--border-focus: var(--color-brand);
|
|
50
54
|
|
|
51
55
|
/* ── Accent ── */
|
|
52
56
|
--accent: var(--color-brand);
|
|
53
57
|
--accent-light: var(--color-brand-light);
|
|
54
|
-
--accent-muted: light-dark(rgba(
|
|
55
|
-
--accent-text: light-dark(#
|
|
58
|
+
--accent-muted: light-dark(rgba(74, 124, 201, 0.12), rgba(106, 159, 224, 0.18));
|
|
59
|
+
--accent-text: light-dark(#3B66A8, #8BB5EA);
|
|
60
|
+
|
|
61
|
+
/* ── Selection / highlight (used for selected rows, items, filters) ── */
|
|
62
|
+
--selection-bg: light-dark(rgba(74, 124, 201, 0.10), rgba(106, 159, 224, 0.15));
|
|
63
|
+
--selection-bg-strong: light-dark(rgba(74, 124, 201, 0.20), rgba(106, 159, 224, 0.25));
|
|
64
|
+
--selection-text: light-dark(#3B66A8, #8BB5EA);
|
|
65
|
+
--selection-border: light-dark(rgba(74, 124, 201, 0.30), rgba(106, 159, 224, 0.30));
|
|
66
|
+
|
|
67
|
+
/* ── Glow ── */
|
|
68
|
+
--glow-brand: light-dark(rgba(74, 124, 201, 0.2), rgba(106, 159, 224, 0.25));
|
|
69
|
+
--glow-brand-sm: 0 0 8px var(--glow-brand);
|
|
70
|
+
--glow-brand-md: 0 0 16px var(--glow-brand);
|
|
56
71
|
|
|
57
72
|
/* ── Shadows (light defaults — can't use light-dark() for comma-separated values) ── */
|
|
58
73
|
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.02);
|
|
59
74
|
--shadow-md: 0 4px 12px -2px rgba(0,0,0,0.06), 0 2px 4px rgba(0,0,0,0.03);
|
|
60
75
|
--shadow-lg: 0 12px 32px -6px rgba(0,0,0,0.08), 0 4px 8px rgba(0,0,0,0.04);
|
|
76
|
+
--shadow-drawer: -12px 0 40px rgba(0,0,0,0.15), -4px 0 12px rgba(0,0,0,0.08);
|
|
61
77
|
|
|
62
78
|
/* ── Topology ── */
|
|
63
|
-
--topology-node-border: light-dark(rgba(0, 0, 0, 0.
|
|
79
|
+
--topology-node-border: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.08));
|
|
64
80
|
--topology-node-shadow: 0 2px 6px rgba(0,0,0,0.05), 0 6px 16px rgba(0,0,0,0.04);
|
|
65
|
-
--topology-bg-dots: light-dark(rgba(
|
|
81
|
+
--topology-bg-dots: light-dark(rgba(126, 130, 138, 0.2), #1E2338);
|
|
66
82
|
|
|
67
83
|
/* Topology edge colors (theme-independent identity colors) */
|
|
68
84
|
--edge-routes-to: #22c55e;
|
|
@@ -107,15 +123,16 @@
|
|
|
107
123
|
--ansi-bright-white: light-dark(#e5e5e5, #e5e5e5);
|
|
108
124
|
|
|
109
125
|
/* ── Scrollbar ── */
|
|
110
|
-
--scrollbar-track: light-dark(#
|
|
111
|
-
--scrollbar-thumb: light-dark(#
|
|
112
|
-
--scrollbar-thumb-hover: light-dark(#
|
|
126
|
+
--scrollbar-track: light-dark(#E6ECF5, #0F1322);
|
|
127
|
+
--scrollbar-thumb: light-dark(#CDD6E8, #2C324A);
|
|
128
|
+
--scrollbar-thumb-hover: light-dark(#AABACF, #484C55);
|
|
113
129
|
}
|
|
114
130
|
|
|
115
131
|
/* Dark overrides for comma-separated values that can't use light-dark() */
|
|
116
132
|
.dark {
|
|
117
|
-
--shadow-sm: 0 1px 3px rgba(0,0,0,0.
|
|
118
|
-
--shadow-md: 0 4px 12px -2px rgba(0,0,0,0.
|
|
133
|
+
--shadow-sm: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2), 0 0 0 1px rgba(255,255,255,0.06);
|
|
134
|
+
--shadow-md: 0 4px 12px -2px rgba(0,0,0,0.35), 0 2px 4px rgba(0,0,0,0.15), 0 0 0 1px rgba(255,255,255,0.06);
|
|
119
135
|
--shadow-lg: 0 12px 32px -6px rgba(0,0,0,0.3), 0 4px 8px rgba(0,0,0,0.1);
|
|
136
|
+
--shadow-drawer: -16px 0 60px rgba(0,0,0,0.6), -6px 0 20px rgba(0,0,0,0.4);
|
|
120
137
|
--topology-node-shadow: 0 2px 6px rgba(0,0,0,0.2), 0 6px 16px rgba(0,0,0,0.15);
|
|
121
138
|
}
|
package/src/types/core.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface Capabilities {
|
|
|
34
34
|
nodeWrite: boolean // Node write operations (cordon, uncordon, drain)
|
|
35
35
|
mcpEnabled: boolean // MCP server is running
|
|
36
36
|
resources?: ResourcePermissions // Per-resource-type permissions
|
|
37
|
+
authEnabled?: boolean // Auth is enabled on the backend
|
|
38
|
+
username?: string // Authenticated user's username (when auth enabled)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
// Core node kinds that have specific UI handling
|