@skyhook-io/radar-app 1.8.0 → 1.8.2
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 +2 -2
- package/src/App.tsx +95 -16
- package/src/api/client.ts +64 -3
- package/src/components/DebugOverlay.tsx +1 -1
- package/src/components/applications/ApplicationsView.tsx +1 -1
- package/src/components/compare/CompareViewRoute.tsx +13 -5
- package/src/components/cost/CostTrendChart.tsx +3 -3
- package/src/components/cost/CostView.tsx +1 -1
- package/src/components/helm/ChartBrowser.tsx +1 -1
- package/src/components/helm/HelmReleaseDrawer.tsx +233 -19
- package/src/components/helm/HelmView.tsx +85 -16
- package/src/components/helm/InstallWizard.tsx +1 -1
- package/src/components/helm/RevisionHistory.tsx +46 -2
- package/src/components/helm/TrackChartSourceDialog.tsx +141 -0
- package/src/components/home/ActivitySummary.tsx +4 -1
- package/src/components/home/TrafficSummary.tsx +2 -2
- package/src/components/home/mcpToolCatalog.ts +5 -5
- package/src/components/issues/IssuesPane.tsx +2 -2
- package/src/components/nav/PrimaryNavRail.tsx +1 -1
- package/src/components/portforward/PortForwardManager.tsx +70 -8
- package/src/components/resource/PrometheusChartsGrid.tsx +1 -1
- package/src/components/resources/ResourcesView.tsx +2 -0
- package/src/components/settings/MyPermissionsDialog.tsx +64 -4
- package/src/components/shared/LargeClusterNamespacePicker.tsx +1 -1
- package/src/components/traffic/TrafficGraph.tsx +29 -20
- package/src/components/traffic/TrafficView.tsx +3 -3
- package/src/components/ui/DiagnosticsOverlay.tsx +1 -1
- package/src/components/ui/ShortcutHelpOverlay.tsx +1 -1
- package/src/components/ui/command-items.ts +1 -1
- package/src/components/workload/WorkloadView.tsx +17 -17
- package/src/context/ConnectionContext.tsx +29 -2
- package/src/main.tsx +1 -1
|
@@ -91,7 +91,7 @@ const VIEW_ENTRIES: { view: MainView; label: string; icon: React.ComponentType<{
|
|
|
91
91
|
{ view: 'timeline', label: 'Timeline', icon: Clock, shortcut: 'g l' },
|
|
92
92
|
{ view: 'helm', label: 'Helm', icon: Package, shortcut: 'g m' },
|
|
93
93
|
{ view: 'gitops', label: 'GitOps', icon: GitBranch, shortcut: 'g o' },
|
|
94
|
-
{ view: 'traffic', label: 'Traffic', icon: Activity, shortcut: 'g f' },
|
|
94
|
+
{ view: 'traffic', label: 'Live Traffic', icon: Activity, shortcut: 'g f' },
|
|
95
95
|
{ view: 'checks', label: 'Checks', icon: ShieldCheck, shortcut: 'g u' },
|
|
96
96
|
{ view: 'cost', label: 'Cost', icon: DollarSign, shortcut: 'g c' },
|
|
97
97
|
]
|
|
@@ -199,45 +199,45 @@ function useActionsBarProps(kind: string, namespace: string, name: string) {
|
|
|
199
199
|
renderPortForward: ({ type, namespace: ns, name: n, className }: { type: 'pod' | 'service'; namespace: string; name: string; className?: string }) => (
|
|
200
200
|
<PortForwardButton type={type} namespace={ns} name={n} className={className} />
|
|
201
201
|
),
|
|
202
|
-
onDelete: (params:
|
|
202
|
+
onDelete: (params: Parameters<typeof deleteMutation.mutate>[0], callbacks?: { onSuccess?: () => void }) => deleteMutation.mutate(params, { onSuccess: callbacks?.onSuccess }),
|
|
203
203
|
isDeleting: deleteMutation.isPending,
|
|
204
204
|
cascadeDependents: cascadePreview?.dependents,
|
|
205
205
|
cascadeLoading,
|
|
206
|
-
onRestart: (params:
|
|
206
|
+
onRestart: (params: Parameters<typeof restartWorkloadMutation.mutate>[0]) => restartWorkloadMutation.mutate(params),
|
|
207
207
|
isRestarting: restartWorkloadMutation.isPending,
|
|
208
208
|
revisions: revisionsList,
|
|
209
209
|
revisionsLoading,
|
|
210
210
|
revisionsError: revisionsError ?? null,
|
|
211
|
-
onRollback: (params:
|
|
211
|
+
onRollback: (params: Parameters<typeof rollbackMutation.mutate>[0], callbacks?: { onSuccess?: () => void }) => rollbackMutation.mutate(params, { onSuccess: callbacks?.onSuccess }),
|
|
212
212
|
isRollingBack: rollbackMutation.isPending,
|
|
213
|
-
onTriggerCronJob: (params:
|
|
213
|
+
onTriggerCronJob: (params: Parameters<typeof triggerCronJobMutation.mutate>[0]) => triggerCronJobMutation.mutate(params),
|
|
214
214
|
isTriggeringCronJob: triggerCronJobMutation.isPending,
|
|
215
|
-
onSuspendCronJob: (params:
|
|
215
|
+
onSuspendCronJob: (params: Parameters<typeof suspendCronJobMutation.mutate>[0]) => suspendCronJobMutation.mutate(params),
|
|
216
216
|
isSuspendingCronJob: suspendCronJobMutation.isPending,
|
|
217
|
-
onResumeCronJob: (params:
|
|
217
|
+
onResumeCronJob: (params: Parameters<typeof resumeCronJobMutation.mutate>[0]) => resumeCronJobMutation.mutate(params),
|
|
218
218
|
isResumingCronJob: resumeCronJobMutation.isPending,
|
|
219
|
-
onFluxReconcile: (params:
|
|
219
|
+
onFluxReconcile: (params: Parameters<typeof fluxReconcileMutation.mutate>[0]) => fluxReconcileMutation.mutate(params),
|
|
220
220
|
isFluxReconciling: fluxReconcileMutation.isPending,
|
|
221
|
-
onFluxSyncWithSource: (params:
|
|
221
|
+
onFluxSyncWithSource: (params: Parameters<typeof fluxSyncWithSourceMutation.mutate>[0]) => fluxSyncWithSourceMutation.mutate(params),
|
|
222
222
|
isFluxSyncing: fluxSyncWithSourceMutation.isPending,
|
|
223
|
-
onFluxSuspend: (params:
|
|
223
|
+
onFluxSuspend: (params: Parameters<typeof fluxSuspendMutation.mutate>[0]) => fluxSuspendMutation.mutate(params),
|
|
224
224
|
isFluxSuspending: fluxSuspendMutation.isPending,
|
|
225
|
-
onFluxResume: (params:
|
|
225
|
+
onFluxResume: (params: Parameters<typeof fluxResumeMutation.mutate>[0]) => fluxResumeMutation.mutate(params),
|
|
226
226
|
isFluxResuming: fluxResumeMutation.isPending,
|
|
227
|
-
onArgoSync: (params:
|
|
227
|
+
onArgoSync: (params: Parameters<typeof argoSyncMutation.mutate>[0]) => argoSyncMutation.mutate(params),
|
|
228
228
|
isArgoSyncing: argoSyncMutation.isPending,
|
|
229
|
-
onArgoRefresh: (params:
|
|
229
|
+
onArgoRefresh: (params: Parameters<typeof argoRefreshMutation.mutate>[0]) => argoRefreshMutation.mutate(params),
|
|
230
230
|
isArgoRefreshing: argoRefreshMutation.isPending,
|
|
231
|
-
onArgoSuspend: (params:
|
|
231
|
+
onArgoSuspend: (params: Parameters<typeof argoSuspendMutation.mutate>[0]) => argoSuspendMutation.mutate(params),
|
|
232
232
|
isArgoSuspending: argoSuspendMutation.isPending,
|
|
233
|
-
onArgoResume: (params:
|
|
233
|
+
onArgoResume: (params: Parameters<typeof argoResumeMutation.mutate>[0]) => argoResumeMutation.mutate(params),
|
|
234
234
|
isArgoResuming: argoResumeMutation.isPending,
|
|
235
235
|
canNodeWrite,
|
|
236
|
-
onCordonNode: (params:
|
|
236
|
+
onCordonNode: (params: Parameters<typeof cordonMutation.mutate>[0]) => cordonMutation.mutate(params),
|
|
237
237
|
isCordoningNode: cordonMutation.isPending,
|
|
238
|
-
onUncordonNode: (params:
|
|
238
|
+
onUncordonNode: (params: Parameters<typeof uncordonMutation.mutate>[0]) => uncordonMutation.mutate(params),
|
|
239
239
|
isUncordoningNode: uncordonMutation.isPending,
|
|
240
|
-
onDrainNode: (params:
|
|
240
|
+
onDrainNode: (params: Parameters<typeof drainMutation.mutate>[0]) => drainMutation.mutate(params),
|
|
241
241
|
isDrainingNode: drainMutation.isPending,
|
|
242
242
|
}
|
|
243
243
|
}
|
|
@@ -62,6 +62,22 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
62
62
|
// Track if SSE has started delivering connection_state events
|
|
63
63
|
// Once SSE is active, it becomes the authoritative source for connection state
|
|
64
64
|
const sseActiveRef = useRef(false)
|
|
65
|
+
// Track whether we've reached 'connected' at least once. Distinguishes the
|
|
66
|
+
// initial connect (bootstrap queries already fetched while 'connecting') from
|
|
67
|
+
// a reconnect after a drop (cache may be stale across the gap).
|
|
68
|
+
const hasConnectedRef = useRef(false)
|
|
69
|
+
// Whether the QueryClient already held data when this provider mounted. A host
|
|
70
|
+
// can share one client across cluster-scoped RadarApp mounts (see RadarApp's
|
|
71
|
+
// `queryClient` prop); that client may carry another cluster's data under
|
|
72
|
+
// identical keys, so a warm-at-mount cache must be fully refreshed on first
|
|
73
|
+
// connect. A cold cache (standalone, or a per-cluster remount) takes the cheap
|
|
74
|
+
// error-only path. Snapshot synchronously before this provider's own query
|
|
75
|
+
// registers — ConnectionProvider is the outermost provider, so a fresh client
|
|
76
|
+
// is genuinely empty here.
|
|
77
|
+
const cacheWarmAtMountRef = useRef<boolean | null>(null)
|
|
78
|
+
if (cacheWarmAtMountRef.current === null) {
|
|
79
|
+
cacheWarmAtMountRef.current = queryClient.getQueryCache().getAll().length > 0
|
|
80
|
+
}
|
|
65
81
|
|
|
66
82
|
// Fetch initial connection status
|
|
67
83
|
// Poll while connecting to get progress updates (SSE not established yet)
|
|
@@ -143,9 +159,20 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
143
159
|
return status
|
|
144
160
|
})
|
|
145
161
|
|
|
146
|
-
// If we just connected, invalidate queries to fetch fresh data
|
|
147
162
|
if (status.state === 'connected') {
|
|
148
|
-
|
|
163
|
+
const firstConnect = !hasConnectedRef.current
|
|
164
|
+
hasConnectedRef.current = true
|
|
165
|
+
// A reconnect after a drop (cache stale across the gap), or a first connect
|
|
166
|
+
// onto a client that already carried data at mount (shared across clusters),
|
|
167
|
+
// refreshes the whole cache. A clean first connect only needs to recover the
|
|
168
|
+
// bootstrap queries that 503'd while the cluster was still 'connecting'
|
|
169
|
+
// (status === 'error'); the rest already fetched fresh during 'connecting',
|
|
170
|
+
// so re-fetching the whole cache there would double-load every endpoint.
|
|
171
|
+
if (!firstConnect || cacheWarmAtMountRef.current) {
|
|
172
|
+
queryClient.invalidateQueries()
|
|
173
|
+
} else {
|
|
174
|
+
queryClient.invalidateQueries({ predicate: (q) => q.state.status === 'error' })
|
|
175
|
+
}
|
|
149
176
|
}
|
|
150
177
|
}, [queryClient])
|
|
151
178
|
|
package/src/main.tsx
CHANGED
|
@@ -130,7 +130,7 @@ document.execCommand = function (command: string, showUI?: boolean, value?: stri
|
|
|
130
130
|
dt.setData('text/plain', text)
|
|
131
131
|
const ev = new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true })
|
|
132
132
|
if (!el.dispatchEvent(ev)) return
|
|
133
|
-
} catch
|
|
133
|
+
} catch { /* ClipboardEvent dispatch failed, fall back to insertText */ }
|
|
134
134
|
_origExecCommand('insertText', false, text)
|
|
135
135
|
}).catch((err) => { console.warn('[Radar] Paste failed:', err) })
|
|
136
136
|
return true
|