@skyhook-io/k8s-ui 1.5.9 → 1.5.10
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/k8s-ui",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.10",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/skyhook-io/radar",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@monaco-editor/react": "^4.7.0",
|
|
60
60
|
"html-to-image": "^1.11.0",
|
|
61
|
-
"react-virtuoso": "^4.18.
|
|
61
|
+
"react-virtuoso": "^4.18.6",
|
|
62
62
|
"shiki": "^4.0.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"clsx": "^2.1.1",
|
|
86
86
|
"diff": "^9.0.0",
|
|
87
87
|
"elkjs": "^0.11.1",
|
|
88
|
-
"lucide-react": "^1.
|
|
88
|
+
"lucide-react": "^1.12.0",
|
|
89
89
|
"react": "^19.2.4",
|
|
90
90
|
"react-dom": "^19.2.4",
|
|
91
91
|
"typescript": "^6.0.2",
|
|
@@ -190,7 +190,7 @@ function FlowRow({
|
|
|
190
190
|
const isIngress = direction === 'ingress'
|
|
191
191
|
|
|
192
192
|
const sourceNodes = (
|
|
193
|
-
<div className="
|
|
193
|
+
<div className="min-w-0 max-w-full shrink space-y-1">
|
|
194
194
|
{sources.map((peer, i) => (
|
|
195
195
|
<div key={i}>
|
|
196
196
|
{i > 0 && (
|
|
@@ -199,13 +199,13 @@ function FlowRow({
|
|
|
199
199
|
<Tooltip content={peer.sublabel ? `${peer.label}\n${peer.sublabel}` : peer.label} position="top">
|
|
200
200
|
<div
|
|
201
201
|
className={clsx(
|
|
202
|
-
'rounded-md border px-2 py-1.5',
|
|
202
|
+
'rounded-md border px-2 py-1.5 min-w-0 overflow-hidden',
|
|
203
203
|
PEER_STYLES[peer.type],
|
|
204
204
|
)}
|
|
205
205
|
>
|
|
206
|
-
<div className="flex items-center gap-1.5">
|
|
206
|
+
<div className="flex items-center gap-1.5 min-w-0">
|
|
207
207
|
<span className={clsx('w-1.5 h-1.5 rounded-full shrink-0', PEER_DOT[peer.type])} />
|
|
208
|
-
<span className={clsx('text-[11px] font-medium truncate', denied && 'line-through text-red-400')}>
|
|
208
|
+
<span className={clsx('text-[11px] font-medium truncate min-w-0', denied && 'line-through text-red-400')}>
|
|
209
209
|
{peer.label}
|
|
210
210
|
</span>
|
|
211
211
|
</div>
|
|
@@ -220,14 +220,16 @@ function FlowRow({
|
|
|
220
220
|
)
|
|
221
221
|
|
|
222
222
|
const targetNode = (
|
|
223
|
-
<
|
|
224
|
-
<
|
|
225
|
-
<div className="
|
|
226
|
-
<
|
|
227
|
-
|
|
223
|
+
<div className="min-w-0 max-w-full shrink-[1000]">
|
|
224
|
+
<Tooltip content={target} position="top">
|
|
225
|
+
<div className="rounded-md border border-indigo-500/30 bg-indigo-500/8 px-2 py-1.5 min-w-0 max-w-full overflow-hidden">
|
|
226
|
+
<div className="flex items-center gap-1.5 min-w-0">
|
|
227
|
+
<span className="w-1.5 h-1.5 rounded-full shrink-0 bg-indigo-500" />
|
|
228
|
+
<span className="text-[11px] font-medium truncate min-w-0">{target}</span>
|
|
229
|
+
</div>
|
|
228
230
|
</div>
|
|
229
|
-
</
|
|
230
|
-
</
|
|
231
|
+
</Tooltip>
|
|
232
|
+
</div>
|
|
231
233
|
)
|
|
232
234
|
|
|
233
235
|
const arrow = (
|
|
@@ -374,72 +374,59 @@ export const K8sResourceNode = memo(function K8sResourceNode({
|
|
|
374
374
|
'pl-3 pr-3',
|
|
375
375
|
isSmallNode ? 'py-2' : 'py-2.5'
|
|
376
376
|
)}>
|
|
377
|
-
{/* Header row: icon + kind label + expand/collapse + status dot */}
|
|
377
|
+
{/* Header row: icon + kind label + (right-aligned) policy badge + expand/collapse + status dot */}
|
|
378
378
|
<div className="flex items-center gap-1.5 mb-0.5">
|
|
379
379
|
<span className={iconClass} />
|
|
380
380
|
<span className="text-[10px] uppercase tracking-wide text-theme-text-tertiary font-medium">
|
|
381
381
|
{isPodGroup ? 'Pod Group' : displayKind(kind)}
|
|
382
382
|
</span>
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
'w-1.5 h-1.5 rounded-full',
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
383
|
+
<div className="ml-auto flex items-center gap-1.5">
|
|
384
|
+
{policyStatus && (
|
|
385
|
+
<Tooltip content={policyStatus === 'protected' ? 'Protected by NetworkPolicy' : 'No NetworkPolicy coverage'} position="right">
|
|
386
|
+
<span className={clsx(
|
|
387
|
+
'inline-flex items-center justify-center w-3 h-3 rounded-sm text-[8px] font-bold cursor-help leading-none',
|
|
388
|
+
policyStatus === 'protected'
|
|
389
|
+
? 'bg-green-500/20 text-green-500 border border-green-500/30'
|
|
390
|
+
: 'bg-yellow-500/20 text-yellow-500 border border-yellow-500/30',
|
|
391
|
+
)}>
|
|
392
|
+
{policyStatus === 'protected' ? '✓' : '!'}
|
|
393
|
+
</span>
|
|
394
|
+
</Tooltip>
|
|
395
|
+
)}
|
|
396
|
+
{canExpand && (
|
|
397
|
+
<button
|
|
398
|
+
onClick={(e) => {
|
|
399
|
+
e.stopPropagation()
|
|
400
|
+
onExpand(id)
|
|
401
|
+
}}
|
|
402
|
+
className="p-0.5 hover:bg-theme-elevated rounded transition-colors"
|
|
403
|
+
title="Expand to show individual pods"
|
|
404
|
+
>
|
|
405
|
+
<ChevronDown className="w-3.5 h-3.5 text-theme-text-secondary" />
|
|
406
|
+
</button>
|
|
407
|
+
)}
|
|
408
|
+
{canCollapse && (
|
|
409
|
+
<button
|
|
410
|
+
onClick={(e) => {
|
|
411
|
+
e.stopPropagation()
|
|
412
|
+
onCollapse(id)
|
|
413
|
+
}}
|
|
414
|
+
className="p-0.5 hover:bg-theme-elevated rounded transition-colors"
|
|
415
|
+
title="Collapse back to group"
|
|
416
|
+
>
|
|
417
|
+
<ChevronUp className="w-3.5 h-3.5 text-theme-text-secondary" />
|
|
418
|
+
</button>
|
|
419
|
+
)}
|
|
420
|
+
{issueTooltip ? (
|
|
421
|
+
<Tooltip content={issueTooltip} position="right">
|
|
422
|
+
<span className={clsx('w-1.5 h-1.5 rounded-full cursor-help', getStatusDotColor(status))} />
|
|
423
|
+
</Tooltip>
|
|
424
|
+
) : (
|
|
425
|
+
<span className={clsx('w-1.5 h-1.5 rounded-full', getStatusDotColor(status))} />
|
|
426
|
+
)}
|
|
427
|
+
</div>
|
|
427
428
|
</div>
|
|
428
429
|
|
|
429
|
-
{/* Network Policy coverage indicator */}
|
|
430
|
-
{policyStatus && (
|
|
431
|
-
<Tooltip content={policyStatus === 'protected' ? 'Protected by NetworkPolicy' : 'No NetworkPolicy coverage'} position="right">
|
|
432
|
-
<span className={clsx(
|
|
433
|
-
'inline-flex items-center justify-center w-3.5 h-3.5 rounded-sm text-[8px] font-bold cursor-help',
|
|
434
|
-
policyStatus === 'protected'
|
|
435
|
-
? 'bg-green-500/20 text-green-500 border border-green-500/30'
|
|
436
|
-
: 'bg-yellow-500/20 text-yellow-500 border border-yellow-500/30',
|
|
437
|
-
)}>
|
|
438
|
-
{policyStatus === 'protected' ? '✓' : '!'}
|
|
439
|
-
</span>
|
|
440
|
-
</Tooltip>
|
|
441
|
-
)}
|
|
442
|
-
|
|
443
430
|
{/* Name */}
|
|
444
431
|
<div className="text-sm font-medium text-theme-text-primary truncate pr-1">
|
|
445
432
|
{name}
|
|
@@ -654,12 +654,12 @@ export function TopologyGraph({
|
|
|
654
654
|
}, [selectedNodeId, setNodes])
|
|
655
655
|
|
|
656
656
|
if (!topology) {
|
|
657
|
-
return <PaneLoader label="Loading topology…" className="
|
|
657
|
+
return <PaneLoader label="Loading topology…" className="absolute inset-0" />
|
|
658
658
|
}
|
|
659
659
|
|
|
660
660
|
if (topology.nodes.length === 0) {
|
|
661
661
|
return (
|
|
662
|
-
<div className="
|
|
662
|
+
<div className="absolute inset-0 flex items-center justify-center text-theme-text-secondary">
|
|
663
663
|
<div className="text-center">
|
|
664
664
|
<p className="text-lg">No resources found</p>
|
|
665
665
|
<p className="text-sm mt-2">
|
|
@@ -673,7 +673,7 @@ export function TopologyGraph({
|
|
|
673
673
|
// Show layout error if we have topology data but layout failed
|
|
674
674
|
if (layoutError && nodes.length === 0) {
|
|
675
675
|
return (
|
|
676
|
-
<div className="
|
|
676
|
+
<div className="absolute inset-0 flex items-center justify-center text-theme-text-secondary">
|
|
677
677
|
<div className="text-center max-w-md">
|
|
678
678
|
<p className="text-lg text-amber-400">Layout Error</p>
|
|
679
679
|
<p className="text-sm mt-2">
|
package/src/types/core.ts
CHANGED
|
@@ -33,11 +33,27 @@ export interface Capabilities {
|
|
|
33
33
|
helmWrite: boolean // Helm write operations (install, upgrade, rollback, uninstall, apply values)
|
|
34
34
|
nodeWrite: boolean // Node write operations (cordon, uncordon, drain)
|
|
35
35
|
mcpEnabled: boolean // MCP server is running
|
|
36
|
+
// How / where this Radar binary is running. Optional on the wire so a
|
|
37
|
+
// newer frontend (e.g. radar-hub-web bundling a fresher @skyhook-io/radar-app)
|
|
38
|
+
// doesn't crash against an older backend that hasn't shipped the field yet —
|
|
39
|
+
// consumers should default to { mode: 'local' } when absent.
|
|
40
|
+
deployment?: Deployment
|
|
36
41
|
resources?: ResourcePermissions // Per-resource-type permissions
|
|
37
42
|
authEnabled?: boolean // Auth is enabled on the backend
|
|
38
43
|
username?: string // Authenticated user's username (when auth enabled)
|
|
39
44
|
}
|
|
40
45
|
|
|
46
|
+
// DeploymentMode is the closed set of topologies Radar can run in.
|
|
47
|
+
// `local` is a developer's machine with a kubeconfig (most OSS use).
|
|
48
|
+
// `in-cluster` is a Radar pod inside the cluster, no kubeconfig.
|
|
49
|
+
// `cloud` is in-cluster + tunneled to Radar Cloud's hub — UI suppresses
|
|
50
|
+
// chrome the hub already renders (cluster headline, local-MCP card).
|
|
51
|
+
export type DeploymentMode = 'local' | 'in-cluster' | 'cloud'
|
|
52
|
+
|
|
53
|
+
export interface Deployment {
|
|
54
|
+
mode: DeploymentMode
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
// Core node kinds that have specific UI handling
|
|
42
58
|
//
|
|
43
59
|
// When adding a new kind here, also update:
|