@skyhook-io/k8s-ui 1.0.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 +63 -0
- package/src/components/dock/BottomDock.tsx +204 -0
- package/src/components/dock/DockContext.tsx +212 -0
- package/src/components/dock/TerminalTab.tsx +333 -0
- package/src/components/dock/index.ts +3 -0
- package/src/components/gitops/GitOpsActions.tsx +227 -0
- package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
- package/src/components/gitops/ManagedResourcesList.tsx +210 -0
- package/src/components/gitops/SyncCountdown.tsx +146 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/logs/JsonLogLine.tsx +90 -0
- package/src/components/logs/LogCore.tsx +553 -0
- package/src/components/logs/LogToolbarSelects.tsx +74 -0
- package/src/components/logs/LogsViewer.tsx +146 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
- package/src/components/logs/index.ts +13 -0
- package/src/components/logs/useLogBuffer.ts +138 -0
- package/src/components/logs/useLogSearch.ts +151 -0
- package/src/components/logs/useLogStream.ts +61 -0
- package/src/components/resources/ResourcesView.tsx +5494 -0
- package/src/components/resources/index.ts +17 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
- package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
- package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
- package/src/components/resources/renderers/EventRenderer.tsx +175 -0
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
- package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
- package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
- package/src/components/resources/renderers/HPARenderer.tsx +164 -0
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
- package/src/components/resources/renderers/JobRenderer.tsx +103 -0
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
- package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
- package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
- package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
- package/src/components/resources/renderers/PodRenderer.tsx +677 -0
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
- package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
- package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
- package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
- package/src/components/resources/renderers/VPARenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
- package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
- package/src/components/resources/renderers/argo-cells.tsx +118 -0
- package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
- package/src/components/resources/renderers/eso-cells.tsx +115 -0
- package/src/components/resources/renderers/flux-cells.tsx +253 -0
- package/src/components/resources/renderers/index.ts +105 -0
- package/src/components/resources/renderers/istio-cells.tsx +210 -0
- package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
- package/src/components/resources/renderers/keda-cells.tsx +94 -0
- package/src/components/resources/renderers/knative-cells.tsx +396 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
- package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
- package/src/components/resources/renderers/traefik-cells.tsx +120 -0
- package/src/components/resources/renderers/trivy-cells.tsx +153 -0
- package/src/components/resources/renderers/trivy-shared.tsx +67 -0
- package/src/components/resources/renderers/velero-cells.tsx +164 -0
- package/src/components/resources/resource-utils-argo.ts +155 -0
- package/src/components/resources/resource-utils-certmanager.ts +198 -0
- package/src/components/resources/resource-utils-cnpg.ts +344 -0
- package/src/components/resources/resource-utils-eso.ts +385 -0
- package/src/components/resources/resource-utils-flux.ts +186 -0
- package/src/components/resources/resource-utils-istio.ts +372 -0
- package/src/components/resources/resource-utils-karpenter.ts +150 -0
- package/src/components/resources/resource-utils-keda.ts +203 -0
- package/src/components/resources/resource-utils-knative.ts +218 -0
- package/src/components/resources/resource-utils-kyverno.ts +224 -0
- package/src/components/resources/resource-utils-prometheus.ts +159 -0
- package/src/components/resources/resource-utils-traefik.ts +180 -0
- package/src/components/resources/resource-utils-trivy.ts +146 -0
- package/src/components/resources/resource-utils-velero.ts +293 -0
- package/src/components/resources/resource-utils.ts +1558 -0
- package/src/components/shared/EditableYamlView.tsx +302 -0
- package/src/components/shared/ResourceActionsBar.tsx +924 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/shared.tsx +614 -0
- package/src/components/topology/GroupNode.tsx +198 -0
- package/src/components/topology/K8sResourceNode.tsx +440 -0
- package/src/components/topology/TopologyControls.tsx +62 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
- package/src/components/topology/TopologyGraph.tsx +748 -0
- package/src/components/topology/TopologySearch.tsx +304 -0
- package/src/components/topology/index.ts +7 -0
- package/src/components/topology/layout.ts +734 -0
- package/src/components/topology/layout.worker.ts +272 -0
- package/src/components/topology/topology.css +129 -0
- package/src/components/ui/CodeViewer.tsx +451 -0
- package/src/components/ui/ConfirmDialog.tsx +188 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
- package/src/components/ui/HealthRing.tsx +72 -0
- package/src/components/ui/MetricsChart.tsx +259 -0
- package/src/components/ui/ResourceBar.tsx +68 -0
- package/src/components/ui/Toast.tsx +225 -0
- package/src/components/ui/Tooltip.tsx +165 -0
- package/src/components/ui/YamlEditor.tsx +280 -0
- package/src/components/ui/drawer-components.tsx +792 -0
- package/src/components/ui/index.ts +10 -0
- package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
- package/src/components/workload/WorkloadView.tsx +1109 -0
- package/src/components/workload/index.ts +2 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAnimatedUnmount.ts +30 -0
- package/src/hooks/useKeyboardShortcuts.tsx +336 -0
- package/src/hooks/useRefreshAnimation.ts +58 -0
- package/src/index.ts +35 -0
- package/src/types/core.ts +894 -0
- package/src/types/gitops.ts +458 -0
- package/src/types/index.ts +2 -0
- package/src/utils/animation.ts +40 -0
- package/src/utils/api-resources.ts +185 -0
- package/src/utils/badge-colors.ts +399 -0
- package/src/utils/context-name.ts +72 -0
- package/src/utils/download.ts +19 -0
- package/src/utils/format.ts +233 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/log-format.ts +300 -0
- package/src/utils/navigation.ts +119 -0
- package/src/utils/resource-hierarchy.ts +630 -0
- package/src/utils/resource-icons.ts +206 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useRef, useMemo } from 'react'
|
|
2
|
+
import { Search, X, ChevronRight } from 'lucide-react'
|
|
3
|
+
import { getTopologyIcon } from '../../utils/resource-icons'
|
|
4
|
+
import { clsx } from 'clsx'
|
|
5
|
+
import type { TopologyNode } from '../../types'
|
|
6
|
+
import { useRegisterShortcut } from '../../hooks/useKeyboardShortcuts'
|
|
7
|
+
|
|
8
|
+
interface TopologySearchProps {
|
|
9
|
+
nodes: TopologyNode[]
|
|
10
|
+
onNodeSelect?: (node: TopologyNode) => void
|
|
11
|
+
onZoomToNode?: (nodeId: string) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Icon mapping for different resource kinds
|
|
15
|
+
function getNodeIcon(kind: string) {
|
|
16
|
+
return getTopologyIcon(kind)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Color mapping for different resource kinds
|
|
20
|
+
function getKindColor(kind: string): string {
|
|
21
|
+
switch (kind) {
|
|
22
|
+
case 'Ingress':
|
|
23
|
+
case 'Gateway':
|
|
24
|
+
return 'text-purple-400'
|
|
25
|
+
case 'HTTPRoute':
|
|
26
|
+
case 'GRPCRoute':
|
|
27
|
+
case 'TCPRoute':
|
|
28
|
+
case 'TLSRoute':
|
|
29
|
+
return 'text-purple-300'
|
|
30
|
+
case 'Service':
|
|
31
|
+
return 'text-blue-400'
|
|
32
|
+
case 'Deployment':
|
|
33
|
+
case 'Rollout':
|
|
34
|
+
return 'text-emerald-400'
|
|
35
|
+
case 'DaemonSet':
|
|
36
|
+
return 'text-teal-400'
|
|
37
|
+
case 'StatefulSet':
|
|
38
|
+
return 'text-cyan-400'
|
|
39
|
+
case 'ReplicaSet':
|
|
40
|
+
return 'text-green-400'
|
|
41
|
+
case 'Pod':
|
|
42
|
+
case 'PodGroup':
|
|
43
|
+
return 'text-lime-400'
|
|
44
|
+
case 'ConfigMap':
|
|
45
|
+
return 'text-amber-400'
|
|
46
|
+
case 'Secret':
|
|
47
|
+
return 'text-red-400'
|
|
48
|
+
default:
|
|
49
|
+
return 'text-theme-text-secondary'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function TopologySearch({ nodes, onNodeSelect, onZoomToNode }: TopologySearchProps) {
|
|
54
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
55
|
+
const [query, setQuery] = useState('')
|
|
56
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
57
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
58
|
+
const resultsRef = useRef<HTMLDivElement>(null)
|
|
59
|
+
|
|
60
|
+
// Filter nodes based on query
|
|
61
|
+
const filteredNodes = useMemo(() => {
|
|
62
|
+
if (!query.trim()) return []
|
|
63
|
+
|
|
64
|
+
const lowerQuery = query.toLowerCase()
|
|
65
|
+
return nodes
|
|
66
|
+
.filter(node => {
|
|
67
|
+
const name = node.name.toLowerCase()
|
|
68
|
+
const kind = node.kind.toLowerCase()
|
|
69
|
+
const namespace = (node.data.namespace as string || '').toLowerCase()
|
|
70
|
+
return (
|
|
71
|
+
name.includes(lowerQuery) ||
|
|
72
|
+
kind.includes(lowerQuery) ||
|
|
73
|
+
namespace.includes(lowerQuery) ||
|
|
74
|
+
`${kind}/${name}`.includes(lowerQuery) ||
|
|
75
|
+
`${namespace}/${name}`.includes(lowerQuery)
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
.slice(0, 10) // Limit results
|
|
79
|
+
}, [nodes, query])
|
|
80
|
+
|
|
81
|
+
// Reset selection when results change
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
setSelectedIndex(0)
|
|
84
|
+
}, [filteredNodes])
|
|
85
|
+
|
|
86
|
+
// Scroll selected item into view
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (resultsRef.current && filteredNodes.length > 0) {
|
|
89
|
+
const selectedElement = resultsRef.current.children[selectedIndex] as HTMLElement
|
|
90
|
+
if (selectedElement) {
|
|
91
|
+
selectedElement.scrollIntoView({ block: 'nearest' })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, [selectedIndex, filteredNodes.length])
|
|
95
|
+
|
|
96
|
+
// Keyboard shortcuts for topology search
|
|
97
|
+
useRegisterShortcut({
|
|
98
|
+
id: 'topology-search',
|
|
99
|
+
keys: '/',
|
|
100
|
+
description: 'Search topology',
|
|
101
|
+
category: 'Search',
|
|
102
|
+
scope: 'topology',
|
|
103
|
+
handler: () => {
|
|
104
|
+
setIsOpen(true)
|
|
105
|
+
setTimeout(() => inputRef.current?.focus(), 0)
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
useRegisterShortcut({
|
|
109
|
+
id: 'topology-search-close',
|
|
110
|
+
keys: 'Escape',
|
|
111
|
+
description: 'Close search',
|
|
112
|
+
category: 'Topology',
|
|
113
|
+
scope: 'topology',
|
|
114
|
+
handler: () => { if (isOpen) handleClose() },
|
|
115
|
+
enabled: isOpen,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Navigate to node and zoom
|
|
119
|
+
const navigateToNode = useCallback((node: TopologyNode) => {
|
|
120
|
+
// Zoom to the node
|
|
121
|
+
onZoomToNode?.(node.id)
|
|
122
|
+
|
|
123
|
+
// Notify parent
|
|
124
|
+
onNodeSelect?.(node)
|
|
125
|
+
|
|
126
|
+
// Close search
|
|
127
|
+
handleClose()
|
|
128
|
+
}, [onZoomToNode, onNodeSelect])
|
|
129
|
+
|
|
130
|
+
// Handle keyboard navigation in results
|
|
131
|
+
const handleInputKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
132
|
+
if (!filteredNodes.length) return
|
|
133
|
+
|
|
134
|
+
switch (e.key) {
|
|
135
|
+
case 'ArrowDown':
|
|
136
|
+
e.preventDefault()
|
|
137
|
+
setSelectedIndex(i => Math.min(i + 1, filteredNodes.length - 1))
|
|
138
|
+
break
|
|
139
|
+
case 'ArrowUp':
|
|
140
|
+
e.preventDefault()
|
|
141
|
+
setSelectedIndex(i => Math.max(i - 1, 0))
|
|
142
|
+
break
|
|
143
|
+
case 'Enter':
|
|
144
|
+
e.preventDefault()
|
|
145
|
+
if (filteredNodes[selectedIndex]) {
|
|
146
|
+
navigateToNode(filteredNodes[selectedIndex])
|
|
147
|
+
}
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}, [filteredNodes, selectedIndex, navigateToNode])
|
|
151
|
+
|
|
152
|
+
const handleClose = useCallback(() => {
|
|
153
|
+
setIsOpen(false)
|
|
154
|
+
setQuery('')
|
|
155
|
+
setSelectedIndex(0)
|
|
156
|
+
}, [])
|
|
157
|
+
|
|
158
|
+
const handleOpen = useCallback(() => {
|
|
159
|
+
setIsOpen(true)
|
|
160
|
+
setTimeout(() => inputRef.current?.focus(), 0)
|
|
161
|
+
}, [])
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
{/* Search trigger button */}
|
|
166
|
+
<button
|
|
167
|
+
onClick={handleOpen}
|
|
168
|
+
className="absolute top-4 left-4 z-10 flex items-center gap-2 px-3 py-2 bg-theme-surface/90 backdrop-blur border border-theme-border rounded-lg text-theme-text-secondary hover:text-theme-text-primary hover:border-theme-border-light transition-colors"
|
|
169
|
+
>
|
|
170
|
+
<Search className="w-4 h-4" />
|
|
171
|
+
<span className="text-sm">Search</span>
|
|
172
|
+
<kbd className="hidden sm:inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs bg-theme-elevated rounded border border-theme-border-light">
|
|
173
|
+
<span className="text-[10px]">⌘</span>K
|
|
174
|
+
</kbd>
|
|
175
|
+
</button>
|
|
176
|
+
|
|
177
|
+
{/* Search modal */}
|
|
178
|
+
{isOpen && (
|
|
179
|
+
<div className="absolute inset-0 z-50 flex items-start justify-center pt-[10vh]">
|
|
180
|
+
{/* Backdrop */}
|
|
181
|
+
<div
|
|
182
|
+
className="absolute inset-0 bg-theme-base/60 backdrop-blur-sm"
|
|
183
|
+
onClick={handleClose}
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
{/* Search panel */}
|
|
187
|
+
<div className="relative w-full max-w-lg mx-4 bg-theme-surface border border-theme-border rounded-xl shadow-2xl overflow-hidden">
|
|
188
|
+
{/* Search input */}
|
|
189
|
+
<div className="flex items-center gap-3 px-4 py-3 border-b border-theme-border">
|
|
190
|
+
<Search className="w-5 h-5 text-theme-text-secondary" />
|
|
191
|
+
<input
|
|
192
|
+
ref={inputRef}
|
|
193
|
+
type="text"
|
|
194
|
+
value={query}
|
|
195
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
196
|
+
onKeyDown={handleInputKeyDown}
|
|
197
|
+
placeholder="Search resources by name, kind, or namespace..."
|
|
198
|
+
className="flex-1 bg-transparent text-theme-text-primary placeholder-theme-text-disabled outline-none text-sm"
|
|
199
|
+
autoFocus
|
|
200
|
+
/>
|
|
201
|
+
{query && (
|
|
202
|
+
<button
|
|
203
|
+
onClick={() => setQuery('')}
|
|
204
|
+
className="p-1 text-theme-text-secondary hover:text-theme-text-primary"
|
|
205
|
+
>
|
|
206
|
+
<X className="w-4 h-4" />
|
|
207
|
+
</button>
|
|
208
|
+
)}
|
|
209
|
+
<kbd className="px-1.5 py-0.5 text-xs text-theme-text-tertiary bg-theme-elevated rounded border border-theme-border-light">
|
|
210
|
+
ESC
|
|
211
|
+
</kbd>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
{/* Results */}
|
|
215
|
+
<div ref={resultsRef} className="max-h-[60vh] overflow-y-auto">
|
|
216
|
+
{query && filteredNodes.length === 0 && (
|
|
217
|
+
<div className="px-4 py-8 text-center text-theme-text-tertiary">
|
|
218
|
+
<Search className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
219
|
+
<p>No resources found for "{query}"</p>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{filteredNodes.map((node, index) => {
|
|
224
|
+
const Icon = getNodeIcon(node.kind)
|
|
225
|
+
const isSelected = index === selectedIndex
|
|
226
|
+
const namespace = node.data.namespace as string || ''
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<button
|
|
230
|
+
key={node.id}
|
|
231
|
+
onClick={() => navigateToNode(node)}
|
|
232
|
+
onMouseEnter={() => setSelectedIndex(index)}
|
|
233
|
+
className={clsx(
|
|
234
|
+
'w-full flex items-center gap-3 px-4 py-3 text-left transition-colors',
|
|
235
|
+
isSelected ? 'bg-theme-elevated/50' : 'hover:bg-theme-elevated/30'
|
|
236
|
+
)}
|
|
237
|
+
>
|
|
238
|
+
<div className={clsx(
|
|
239
|
+
'flex items-center justify-center w-8 h-8 rounded-lg',
|
|
240
|
+
isSelected ? 'bg-blue-500/20' : 'bg-theme-elevated/50'
|
|
241
|
+
)}>
|
|
242
|
+
<Icon className={clsx('w-4 h-4', getKindColor(node.kind))} />
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div className="flex-1 min-w-0">
|
|
246
|
+
<div className="flex items-center gap-2">
|
|
247
|
+
<span className="font-medium text-theme-text-primary truncate">{node.name}</span>
|
|
248
|
+
<span className={clsx(
|
|
249
|
+
'px-1.5 py-0.5 text-xs rounded',
|
|
250
|
+
'bg-theme-elevated text-theme-text-secondary'
|
|
251
|
+
)}>
|
|
252
|
+
{node.kind}
|
|
253
|
+
</span>
|
|
254
|
+
</div>
|
|
255
|
+
{namespace && (
|
|
256
|
+
<div className="text-xs text-theme-text-tertiary truncate">
|
|
257
|
+
{namespace}
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<ChevronRight className={clsx(
|
|
263
|
+
'w-4 h-4 transition-opacity',
|
|
264
|
+
isSelected ? 'text-theme-text-secondary opacity-100' : 'opacity-0'
|
|
265
|
+
)} />
|
|
266
|
+
</button>
|
|
267
|
+
)
|
|
268
|
+
})}
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{/* Footer hint */}
|
|
272
|
+
{filteredNodes.length > 0 && (
|
|
273
|
+
<div className="px-4 py-2 border-t border-theme-border bg-theme-surface/50">
|
|
274
|
+
<div className="flex items-center gap-4 text-xs text-theme-text-tertiary">
|
|
275
|
+
<span className="flex items-center gap-1">
|
|
276
|
+
<kbd className="px-1 py-0.5 bg-theme-elevated rounded">↑</kbd>
|
|
277
|
+
<kbd className="px-1 py-0.5 bg-theme-elevated rounded">↓</kbd>
|
|
278
|
+
<span>Navigate</span>
|
|
279
|
+
</span>
|
|
280
|
+
<span className="flex items-center gap-1">
|
|
281
|
+
<kbd className="px-1.5 py-0.5 bg-theme-elevated rounded">Enter</kbd>
|
|
282
|
+
<span>Zoom to resource</span>
|
|
283
|
+
</span>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
)}
|
|
287
|
+
|
|
288
|
+
{/* Empty state with hints */}
|
|
289
|
+
{!query && (
|
|
290
|
+
<div className="px-4 py-6 text-center text-theme-text-tertiary">
|
|
291
|
+
<p className="text-sm mb-3">Search for resources in the topology</p>
|
|
292
|
+
<div className="flex flex-wrap justify-center gap-2 text-xs">
|
|
293
|
+
<span className="px-2 py-1 bg-theme-elevated/50 rounded">nginx</span>
|
|
294
|
+
<span className="px-2 py-1 bg-theme-elevated/50 rounded">service/api</span>
|
|
295
|
+
<span className="px-2 py-1 bg-theme-elevated/50 rounded">production</span>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
)}
|
|
302
|
+
</>
|
|
303
|
+
)
|
|
304
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { TopologyGraph } from './TopologyGraph'
|
|
2
|
+
export { K8sResourceNode, NODE_DIMENSIONS, DEFAULT_NODE_DIMENSIONS } from './K8sResourceNode'
|
|
3
|
+
export { GroupNode } from './GroupNode'
|
|
4
|
+
export { TopologySearch } from './TopologySearch'
|
|
5
|
+
export { TopologyFilterSidebar } from './TopologyFilterSidebar'
|
|
6
|
+
export { TopologyControls } from './TopologyControls'
|
|
7
|
+
export { buildHierarchicalElkGraph, applyHierarchicalLayout, getGroupKey, setLayoutEngine } from './layout'
|