@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/k8s-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/skyhook-io/radar",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./topology": "./src/components/topology/index.ts",
|
|
23
23
|
"./theme/variables.css": "./src/theme/variables.css",
|
|
24
24
|
"./theme/tailwind-theme.css": "./src/theme/tailwind-theme.css",
|
|
25
|
+
"./theme/components.css": "./src/theme/components.css",
|
|
25
26
|
"./theme": "./src/theme/index.ts",
|
|
26
27
|
"./topology.css": "./src/components/topology/topology.css"
|
|
27
28
|
},
|
|
@@ -65,6 +66,6 @@
|
|
|
65
66
|
"react-dom": "^19.2.4",
|
|
66
67
|
"typescript": "^5.9.3",
|
|
67
68
|
"vitest": "^4.0.18",
|
|
68
|
-
"yaml": "^2.
|
|
69
|
+
"yaml": "^2.8.3"
|
|
69
70
|
}
|
|
70
71
|
}
|
|
@@ -91,7 +91,7 @@ export function BottomDock({ renderTabContent, leftOffset = 0 }: BottomDockProps
|
|
|
91
91
|
>
|
|
92
92
|
{isExpanded && !isMaximized && (
|
|
93
93
|
<div
|
|
94
|
-
className="absolute top-0 left-0 right-0 h-1 cursor-ns-resize hover:bg-
|
|
94
|
+
className="absolute top-0 left-0 right-0 h-1 cursor-ns-resize hover:bg-skyhook-500/50 transition-colors"
|
|
95
95
|
onMouseDown={handleMouseDown}
|
|
96
96
|
/>
|
|
97
97
|
)}
|
|
@@ -227,7 +227,7 @@ export function LocalTerminalTab({
|
|
|
227
227
|
<div className="text-xs text-theme-text-disabled mb-3">{error}</div>
|
|
228
228
|
<button
|
|
229
229
|
onClick={connect}
|
|
230
|
-
className="flex items-center gap-2 px-3 py-1.5
|
|
230
|
+
className="flex items-center gap-2 px-3 py-1.5 btn-brand text-xs rounded"
|
|
231
231
|
>
|
|
232
232
|
<RefreshCw className="w-3 h-3" />
|
|
233
233
|
Retry
|
|
@@ -101,7 +101,7 @@ export function NodeTerminalTab({
|
|
|
101
101
|
<div className="text-xs text-theme-text-tertiary text-center max-w-md break-all">{error}</div>
|
|
102
102
|
<button
|
|
103
103
|
onClick={createPod}
|
|
104
|
-
className="flex items-center gap-2 px-3 py-1.5
|
|
104
|
+
className="flex items-center gap-2 px-3 py-1.5 btn-brand text-xs rounded"
|
|
105
105
|
>
|
|
106
106
|
<RefreshCw className="w-3 h-3" />
|
|
107
107
|
Retry
|
|
@@ -293,7 +293,7 @@ export function TerminalTab({
|
|
|
293
293
|
<button
|
|
294
294
|
onClick={handleCreateDebugContainer}
|
|
295
295
|
disabled={isCreatingDebug}
|
|
296
|
-
className="flex items-center gap-2 px-3 py-1.5
|
|
296
|
+
className="flex items-center gap-2 px-3 py-1.5 btn-brand text-xs rounded"
|
|
297
297
|
>
|
|
298
298
|
{isCreatingDebug ? (
|
|
299
299
|
<><RefreshCw className="w-3 h-3 animate-spin" />Creating debug container...</>
|
|
@@ -317,7 +317,7 @@ export function TerminalTab({
|
|
|
317
317
|
<div className="text-xs text-theme-text-disabled mb-3">{error}</div>
|
|
318
318
|
<button
|
|
319
319
|
onClick={connect}
|
|
320
|
-
className="flex items-center gap-2 px-3 py-1.5
|
|
320
|
+
className="flex items-center gap-2 px-3 py-1.5 btn-brand text-xs rounded"
|
|
321
321
|
>
|
|
322
322
|
<RefreshCw className="w-3 h-3" />
|
|
323
323
|
Retry
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RefreshCw, Pause, Play, Loader2 } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
|
+
import { SEVERITY_BADGE } from '../../utils/badge-colors'
|
|
3
4
|
|
|
4
5
|
interface GitOpsActionsProps {
|
|
5
6
|
/** The GitOps tool type */
|
|
@@ -53,10 +54,10 @@ export function GitOpsActions({
|
|
|
53
54
|
'flex items-center rounded font-medium transition-colors',
|
|
54
55
|
buttonClass,
|
|
55
56
|
isSyncing
|
|
56
|
-
?
|
|
57
|
+
? `${SEVERITY_BADGE.info} cursor-wait`
|
|
57
58
|
: suspended
|
|
58
59
|
? 'bg-theme-elevated text-theme-text-tertiary cursor-not-allowed'
|
|
59
|
-
:
|
|
60
|
+
: `${SEVERITY_BADGE.info} hover:bg-sky-500/30`
|
|
60
61
|
)}
|
|
61
62
|
title={suspended ? 'Cannot sync while suspended' : syncLabel}
|
|
62
63
|
>
|
|
@@ -79,8 +80,8 @@ export function GitOpsActions({
|
|
|
79
80
|
'flex items-center rounded font-medium transition-colors',
|
|
80
81
|
buttonClass,
|
|
81
82
|
isSuspending
|
|
82
|
-
?
|
|
83
|
-
:
|
|
83
|
+
? `${SEVERITY_BADGE.success} cursor-wait`
|
|
84
|
+
: `${SEVERITY_BADGE.success} hover:bg-emerald-500/30`
|
|
84
85
|
)}
|
|
85
86
|
>
|
|
86
87
|
{isSuspending ? (
|
|
@@ -100,8 +101,8 @@ export function GitOpsActions({
|
|
|
100
101
|
'flex items-center rounded font-medium transition-colors',
|
|
101
102
|
buttonClass,
|
|
102
103
|
isSuspending
|
|
103
|
-
?
|
|
104
|
-
:
|
|
104
|
+
? `${SEVERITY_BADGE.warning} cursor-wait`
|
|
105
|
+
: `${SEVERITY_BADGE.warning} hover:bg-amber-500/30`
|
|
105
106
|
)}
|
|
106
107
|
>
|
|
107
108
|
{isSuspending ? (
|
|
@@ -147,7 +148,7 @@ export function SyncButton({
|
|
|
147
148
|
buttonClass,
|
|
148
149
|
loading || disabled
|
|
149
150
|
? 'bg-theme-elevated text-theme-text-tertiary cursor-not-allowed'
|
|
150
|
-
:
|
|
151
|
+
: `${SEVERITY_BADGE.info} hover:bg-sky-500/30`
|
|
151
152
|
)}
|
|
152
153
|
>
|
|
153
154
|
{loading ? (
|
|
@@ -190,8 +191,8 @@ export function SuspendToggle({
|
|
|
190
191
|
'flex items-center rounded font-medium transition-colors',
|
|
191
192
|
buttonClass,
|
|
192
193
|
loading
|
|
193
|
-
?
|
|
194
|
-
:
|
|
194
|
+
? `${SEVERITY_BADGE.success} cursor-wait`
|
|
195
|
+
: `${SEVERITY_BADGE.success} hover:bg-emerald-500/30`
|
|
195
196
|
)}
|
|
196
197
|
>
|
|
197
198
|
{loading ? (
|
|
@@ -212,8 +213,8 @@ export function SuspendToggle({
|
|
|
212
213
|
'flex items-center rounded font-medium transition-colors',
|
|
213
214
|
buttonClass,
|
|
214
215
|
loading
|
|
215
|
-
?
|
|
216
|
-
:
|
|
216
|
+
? `${SEVERITY_BADGE.warning} cursor-wait`
|
|
217
|
+
: `${SEVERITY_BADGE.warning} hover:bg-amber-500/30`
|
|
217
218
|
)}
|
|
218
219
|
>
|
|
219
220
|
{loading ? (
|
|
@@ -21,7 +21,7 @@ export function GitOpsStatusBadge({ status, showHealth = true, compact = false }
|
|
|
21
21
|
if (compact) {
|
|
22
22
|
return (
|
|
23
23
|
<span
|
|
24
|
-
className={clsx('
|
|
24
|
+
className={clsx('badge', colorClass)}
|
|
25
25
|
title={status.message}
|
|
26
26
|
>
|
|
27
27
|
<Icon className="w-3 h-3" />
|
|
@@ -37,7 +37,7 @@ export function GitOpsStatusBadge({ status, showHealth = true, compact = false }
|
|
|
37
37
|
return (
|
|
38
38
|
<div className="flex items-center gap-2">
|
|
39
39
|
<span
|
|
40
|
-
className={clsx('
|
|
40
|
+
className={clsx('badge', colorClass)}
|
|
41
41
|
title={status.message}
|
|
42
42
|
>
|
|
43
43
|
<Icon className="w-3.5 h-3.5" />
|
|
@@ -94,7 +94,7 @@ function HealthIndicator({ health }: HealthIndicatorProps) {
|
|
|
94
94
|
|
|
95
95
|
return (
|
|
96
96
|
<span
|
|
97
|
-
className={clsx('
|
|
97
|
+
className={clsx('badge-sm', color)}
|
|
98
98
|
title={`Health: ${label}`}
|
|
99
99
|
>
|
|
100
100
|
<Icon className="w-3 h-3" />
|
|
@@ -126,7 +126,7 @@ function getHealthInfo(health: GitOpsHealthStatus) {
|
|
|
126
126
|
export function SyncStatusBadge({ sync, suspended }: { sync: SyncStatus; suspended?: boolean }) {
|
|
127
127
|
if (suspended) {
|
|
128
128
|
return (
|
|
129
|
-
<span className={clsx('
|
|
129
|
+
<span className={clsx('badge', SEVERITY_BADGE_BORDERED.warning)}>
|
|
130
130
|
<Pause className="w-3 h-3" />
|
|
131
131
|
Suspended
|
|
132
132
|
</span>
|
|
@@ -137,7 +137,7 @@ export function SyncStatusBadge({ sync, suspended }: { sync: SyncStatus; suspend
|
|
|
137
137
|
const Icon = config.icon
|
|
138
138
|
|
|
139
139
|
return (
|
|
140
|
-
<span className={clsx('
|
|
140
|
+
<span className={clsx('badge', config.color)}>
|
|
141
141
|
<Icon className={clsx('w-3 h-3', sync === 'Reconciling' && 'animate-spin')} />
|
|
142
142
|
{config.label}
|
|
143
143
|
</span>
|
|
@@ -164,7 +164,7 @@ export function HealthStatusBadge({ health }: { health: GitOpsHealthStatus }) {
|
|
|
164
164
|
const { icon: Icon, color, label } = getHealthInfo(health)
|
|
165
165
|
|
|
166
166
|
return (
|
|
167
|
-
<span className={clsx('
|
|
167
|
+
<span className={clsx('badge', color)}>
|
|
168
168
|
<Icon className={clsx('w-3 h-3', health === 'Progressing' && 'animate-spin')} />
|
|
169
169
|
{label}
|
|
170
170
|
</span>
|
|
@@ -240,7 +240,7 @@ export function LogCore({
|
|
|
240
240
|
<button
|
|
241
241
|
onClick={() => setExpandAllStructured(prev => !prev)}
|
|
242
242
|
className={`p-1.5 rounded transition-colors ${
|
|
243
|
-
expandAllStructured ? '
|
|
243
|
+
expandAllStructured ? 'btn-brand-toggle' : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
|
|
244
244
|
}`}
|
|
245
245
|
>
|
|
246
246
|
<Braces className="w-4 h-4" />
|
|
@@ -253,7 +253,7 @@ export function LogCore({
|
|
|
253
253
|
<button
|
|
254
254
|
onClick={toggleTimestamps}
|
|
255
255
|
className={`p-1.5 rounded transition-colors ${
|
|
256
|
-
showTimestamps ? '
|
|
256
|
+
showTimestamps ? 'btn-brand-toggle' : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
|
|
257
257
|
}`}
|
|
258
258
|
>
|
|
259
259
|
<Clock className="w-4 h-4" />
|
|
@@ -265,7 +265,7 @@ export function LogCore({
|
|
|
265
265
|
<button
|
|
266
266
|
onClick={toggleWrap}
|
|
267
267
|
className={`p-1.5 rounded transition-colors ${
|
|
268
|
-
wordWrap ? '
|
|
268
|
+
wordWrap ? 'btn-brand-toggle' : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
|
|
269
269
|
}`}
|
|
270
270
|
>
|
|
271
271
|
<WrapText className="w-4 h-4" />
|
|
@@ -277,7 +277,7 @@ export function LogCore({
|
|
|
277
277
|
<button
|
|
278
278
|
onClick={() => search.isOpen ? search.close() : search.open()}
|
|
279
279
|
className={`p-1.5 rounded transition-colors ${
|
|
280
|
-
search.isOpen ? '
|
|
280
|
+
search.isOpen ? 'btn-brand-toggle' : 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
|
|
281
281
|
}`}
|
|
282
282
|
>
|
|
283
283
|
<Search className="w-4 h-4" />
|
|
@@ -365,7 +365,7 @@ export function LogCore({
|
|
|
365
365
|
<button
|
|
366
366
|
onClick={search.toggleRegex}
|
|
367
367
|
className={`p-1 rounded transition-colors ${
|
|
368
|
-
search.isRegex ? '
|
|
368
|
+
search.isRegex ? 'btn-brand-toggle' : 'text-theme-text-tertiary hover:text-theme-text-secondary'
|
|
369
369
|
}`}
|
|
370
370
|
>
|
|
371
371
|
<Regex className="w-3.5 h-3.5" />
|
|
@@ -377,7 +377,7 @@ export function LogCore({
|
|
|
377
377
|
<button
|
|
378
378
|
onClick={search.toggleCaseSensitive}
|
|
379
379
|
className={`p-1 rounded transition-colors ${
|
|
380
|
-
search.isCaseSensitive ? '
|
|
380
|
+
search.isCaseSensitive ? 'btn-brand-toggle' : 'text-theme-text-tertiary hover:text-theme-text-secondary'
|
|
381
381
|
}`}
|
|
382
382
|
>
|
|
383
383
|
<CaseSensitive className="w-3.5 h-3.5" />
|
|
@@ -389,7 +389,7 @@ export function LogCore({
|
|
|
389
389
|
<button
|
|
390
390
|
onClick={search.toggleFilterMode}
|
|
391
391
|
className={`p-1 rounded transition-colors ${
|
|
392
|
-
search.isFilterMode ? '
|
|
392
|
+
search.isFilterMode ? 'btn-brand-toggle' : 'text-theme-text-tertiary hover:text-theme-text-secondary'
|
|
393
393
|
}`}
|
|
394
394
|
>
|
|
395
395
|
<Filter className="w-3.5 h-3.5" />
|
|
@@ -484,7 +484,7 @@ export function LogCore({
|
|
|
484
484
|
{!atBottom && (
|
|
485
485
|
<button
|
|
486
486
|
onClick={scrollToBottom}
|
|
487
|
-
className="absolute bottom-4 right-14 px-3 py-1.5
|
|
487
|
+
className="absolute bottom-4 right-14 px-3 py-1.5 btn-brand text-xs rounded-full shadow-lg z-10"
|
|
488
488
|
>
|
|
489
489
|
Scroll to bottom
|
|
490
490
|
</button>
|
|
@@ -131,6 +131,29 @@ export function WorkloadLogsViewer({ name, fetchAll, createStream, forceDark }:
|
|
|
131
131
|
})
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
|
+
onPodAdded: (data: any) => {
|
|
135
|
+
if (data?.pods) {
|
|
136
|
+
const newPods = data.pods as WorkloadPodInfo[]
|
|
137
|
+
setPods(prev => {
|
|
138
|
+
const existing = new Set(prev.map(p => p.name))
|
|
139
|
+
const toAdd = newPods.filter(p => !existing.has(p.name))
|
|
140
|
+
return toAdd.length > 0 ? [...prev, ...toAdd] : prev
|
|
141
|
+
})
|
|
142
|
+
setSelectedPods(prev => {
|
|
143
|
+
const next = new Set(prev)
|
|
144
|
+
newPods.forEach(p => next.add(p.name))
|
|
145
|
+
return next
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
onPodRemoved: (data: any) => {
|
|
150
|
+
if (data?.pod) {
|
|
151
|
+
const removedName = data.pod as string
|
|
152
|
+
setPods(prev => prev.filter(p => p.name !== removedName))
|
|
153
|
+
// Don't remove from selectedPods — keep old log entries visible
|
|
154
|
+
// while new pod logs start flowing in
|
|
155
|
+
}
|
|
156
|
+
},
|
|
134
157
|
},
|
|
135
158
|
'Workload log stream error',
|
|
136
159
|
)
|
|
@@ -152,8 +175,9 @@ export function WorkloadLogsViewer({ name, fetchAll, createStream, forceDark }:
|
|
|
152
175
|
}, [])
|
|
153
176
|
|
|
154
177
|
const toggleAllPods = useCallback(() => {
|
|
155
|
-
|
|
156
|
-
|
|
178
|
+
const allSelected = pods.every(p => selectedPods.has(p.name))
|
|
179
|
+
setSelectedPods(allSelected ? new Set() : new Set(pods.map(p => p.name)))
|
|
180
|
+
}, [selectedPods, pods])
|
|
157
181
|
|
|
158
182
|
const filteredEntries = useMemo(
|
|
159
183
|
() => entries.filter(e => !e.pod || selectedPods.has(e.pod)),
|
|
@@ -196,11 +220,11 @@ export function WorkloadLogsViewer({ name, fetchAll, createStream, forceDark }:
|
|
|
196
220
|
<button
|
|
197
221
|
onClick={() => setShowPodFilter(v => !v)}
|
|
198
222
|
className={`flex items-center gap-1.5 px-2 py-1.5 text-xs rounded transition-colors ${
|
|
199
|
-
showPodFilter ? '
|
|
223
|
+
showPodFilter ? 'btn-brand-toggle' : 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover'
|
|
200
224
|
}`}
|
|
201
225
|
>
|
|
202
226
|
<Filter className="w-3 h-3" />
|
|
203
|
-
<span>{selectedPods.
|
|
227
|
+
<span>{pods.filter(p => selectedPods.has(p.name)).length}/{pods.length} pods</span>
|
|
204
228
|
<ChevronDown className="w-3 h-3" />
|
|
205
229
|
</button>
|
|
206
230
|
|
|
@@ -208,7 +232,7 @@ export function WorkloadLogsViewer({ name, fetchAll, createStream, forceDark }:
|
|
|
208
232
|
<div className="absolute top-full left-0 mt-1 w-64 bg-theme-elevated border border-theme-border rounded-lg shadow-lg z-50 max-h-64 overflow-y-auto">
|
|
209
233
|
<div className="p-2 border-b border-theme-border">
|
|
210
234
|
<button onClick={toggleAllPods} className="text-xs text-blue-400 hover:text-blue-300">
|
|
211
|
-
{
|
|
235
|
+
{pods.every(p => selectedPods.has(p.name)) ? 'Deselect all' : 'Select all'}
|
|
212
236
|
</button>
|
|
213
237
|
</div>
|
|
214
238
|
{pods.map(pod => (
|
|
@@ -6,6 +6,10 @@ export interface LogStreamHandlers {
|
|
|
6
6
|
onConnected?: (data: unknown) => void
|
|
7
7
|
/** Called for each log event with parsed event data */
|
|
8
8
|
onLog: (data: unknown) => void
|
|
9
|
+
/** Called when new pods are discovered during streaming (workload logs only) */
|
|
10
|
+
onPodAdded?: (data: unknown) => void
|
|
11
|
+
/** Called when pods are terminated during streaming (workload logs only) */
|
|
12
|
+
onPodRemoved?: (data: unknown) => void
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
/**
|
|
@@ -45,6 +49,22 @@ export function useLogStream() {
|
|
|
45
49
|
}
|
|
46
50
|
})
|
|
47
51
|
|
|
52
|
+
es.addEventListener('pod_added', (event) => {
|
|
53
|
+
if (handlers.onPodAdded) {
|
|
54
|
+
try { handlers.onPodAdded(JSON.parse((event as MessageEvent).data)) } catch (e) {
|
|
55
|
+
console.error('Failed to parse pod_added event:', e)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
es.addEventListener('pod_removed', (event) => {
|
|
61
|
+
if (handlers.onPodRemoved) {
|
|
62
|
+
try { handlers.onPodRemoved(JSON.parse((event as MessageEvent).data)) } catch (e) {
|
|
63
|
+
console.error('Failed to parse pod_removed event:', e)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
48
68
|
es.addEventListener('end', () => setIsStreaming(false))
|
|
49
69
|
|
|
50
70
|
es.addEventListener('error', (event) => {
|
|
@@ -109,7 +109,7 @@ const ResourceTypeButton = forwardRef<HTMLButtonElement, ResourceTypeButtonProps
|
|
|
109
109
|
className={clsx(
|
|
110
110
|
'w-full flex items-center gap-2 px-2 xl:px-3 py-1.5 rounded-lg text-sm transition-colors group/kind min-w-0',
|
|
111
111
|
isSelected
|
|
112
|
-
? '
|
|
112
|
+
? 'selection-strong selection-text'
|
|
113
113
|
: forbidden
|
|
114
114
|
? 'text-theme-text-disabled hover:bg-theme-elevated hover:text-theme-text-secondary'
|
|
115
115
|
: 'text-theme-text-secondary hover:bg-theme-elevated hover:text-theme-text-primary'
|
|
@@ -146,8 +146,8 @@ const ResourceTypeButton = forwardRef<HTMLButtonElement, ResourceTypeButtonProps
|
|
|
146
146
|
</Tooltip>
|
|
147
147
|
) : (
|
|
148
148
|
<span className={clsx(
|
|
149
|
-
'text-xs py-0.5 rounded text-center',
|
|
150
|
-
isSelected ? 'bg-
|
|
149
|
+
'text-xs py-0.5 rounded text-center font-mono',
|
|
150
|
+
isSelected ? 'bg-skyhook-500/30 selection-text' : 'bg-theme-elevated',
|
|
151
151
|
count < 1000 ? 'w-8' : 'w-9'
|
|
152
152
|
)}>
|
|
153
153
|
{count}
|
|
@@ -357,7 +357,7 @@ export function ResourcesSidebar({
|
|
|
357
357
|
}, [effectiveSelectedKind.name, effectiveSelectedKind.group])
|
|
358
358
|
|
|
359
359
|
return (
|
|
360
|
-
<div className={clsx('w-56 2xl:w-72 bg-theme-surface border-r border-theme-border overflow-y-auto overflow-x-hidden shrink-0', className)}>
|
|
360
|
+
<div className={clsx('w-56 2xl:w-72 bg-theme-surface dark:bg-theme-base border-r border-theme-border overflow-y-auto overflow-x-hidden shrink-0', className)}>
|
|
361
361
|
<div className="px-2 py-2 border-b border-theme-border">
|
|
362
362
|
<div className="relative">
|
|
363
363
|
<Search className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-theme-text-tertiary" />
|
|
@@ -367,7 +367,7 @@ export function ResourcesSidebar({
|
|
|
367
367
|
value={kindFilter}
|
|
368
368
|
onChange={(e) => setKindFilter(e.target.value)}
|
|
369
369
|
onKeyDown={(e) => { if (e.key === 'Escape') { setKindFilter(''); (e.target as HTMLInputElement).blur() } }}
|
|
370
|
-
className="w-full pl-7 pr-7 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-
|
|
370
|
+
className="w-full pl-7 pr-7 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"
|
|
371
371
|
/>
|
|
372
372
|
{kindFilter && (
|
|
373
373
|
<button
|
|
@@ -393,7 +393,7 @@ export function ResourcesSidebar({
|
|
|
393
393
|
)}
|
|
394
394
|
<span className="flex-1 text-left">Favorites</span>
|
|
395
395
|
{!favoritesExpanded && pinned.length > 0 && (
|
|
396
|
-
<span className={clsx('text-xs py-0.5 rounded bg-theme-elevated text-theme-text-secondary font-normal normal-case text-center', pinned.length < 1000 ? 'w-8' : 'w-9')}>
|
|
396
|
+
<span className={clsx('text-xs py-0.5 rounded bg-theme-elevated text-theme-text-secondary font-normal normal-case text-center font-mono', pinned.length < 1000 ? 'w-8' : 'w-9')}>
|
|
397
397
|
{pinned.length}
|
|
398
398
|
</span>
|
|
399
399
|
)}
|
|
@@ -444,7 +444,7 @@ export function ResourcesSidebar({
|
|
|
444
444
|
)}
|
|
445
445
|
<span className="flex-1 text-left">{category.name}</span>
|
|
446
446
|
{!isExpanded && (
|
|
447
|
-
<span className={clsx('text-xs py-0.5 rounded bg-theme-elevated text-theme-text-secondary font-normal normal-case text-center', category.total < 1000 ? 'w-8' : 'w-9')}>
|
|
447
|
+
<span className={clsx('text-xs py-0.5 rounded bg-theme-elevated text-theme-text-secondary font-normal normal-case text-center font-mono', category.total < 1000 ? 'w-8' : 'w-9')}>
|
|
448
448
|
{category.total}
|
|
449
449
|
</span>
|
|
450
450
|
)}
|
|
@@ -497,15 +497,15 @@ export function ResourcesSidebar({
|
|
|
497
497
|
className={clsx(
|
|
498
498
|
'w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors',
|
|
499
499
|
isSelected
|
|
500
|
-
? '
|
|
500
|
+
? 'selection-strong selection-text'
|
|
501
501
|
: 'text-theme-text-secondary hover:bg-theme-elevated hover:text-theme-text-primary'
|
|
502
502
|
)}
|
|
503
503
|
>
|
|
504
504
|
<Icon className="w-4 h-4 shrink-0" />
|
|
505
505
|
<span className="flex-1 text-left">{type.label}</span>
|
|
506
506
|
<span className={clsx(
|
|
507
|
-
'
|
|
508
|
-
isSelected ? 'bg-
|
|
507
|
+
'badge font-mono',
|
|
508
|
+
isSelected ? 'bg-skyhook-500/30 selection-text' : 'bg-theme-elevated'
|
|
509
509
|
)}>
|
|
510
510
|
{count}
|
|
511
511
|
</span>
|