@skyhook-io/radar-app 1.8.3 → 1.8.6
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 +5 -5
- package/src/App.tsx +256 -94
- package/src/RadarApp.tsx +4 -1
- package/src/api/client.metrics.test.ts +106 -0
- package/src/api/client.ts +147 -20
- package/src/components/ConnectionErrorView.tsx +1 -1
- package/src/components/ContextSwitcher.tsx +5 -1
- package/src/components/NamespaceSwitcher.tsx +21 -300
- package/src/components/applications/ApplicationsView.tsx +22 -7
- package/src/components/audit/AuditView.tsx +11 -2
- package/src/components/cost/CostView.tsx +12 -2
- package/src/components/gitops/GitOpsView.tsx +22 -7
- package/src/components/helm/HelmCompareRoute.tsx +1342 -0
- package/src/components/helm/HelmReleaseDrawer.tsx +189 -352
- package/src/components/helm/HelmView.tsx +79 -62
- package/src/components/helm/ManifestDiffViewer.tsx +4 -4
- package/src/components/home/ClusterHealthCard.tsx +6 -1
- package/src/components/home/HomeView.tsx +20 -7
- package/src/components/home/mcpToolCatalog.ts +1 -1
- package/src/components/issues/IssuesPane.tsx +29 -18
- package/src/components/resources/ResourceDetailDrawer.tsx +8 -3
- package/src/components/resources/ResourcesView.tsx +3 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +10 -4
- package/src/components/resources/renderers/PodRenderer.tsx +10 -4
- package/src/components/timeline/TimelineView.tsx +26 -2
- package/src/components/traffic/TrafficView.tsx +17 -10
- package/src/components/ui/Markdown.tsx +2 -2
- package/src/components/ui/Omnibar.tsx +1 -1
- package/src/components/workload/WorkloadView.tsx +5 -1
- package/src/filter/FilterLocationBridge.tsx +30 -0
- package/src/hooks/useKeyboardShortcuts.tsx +1 -0
- package/src/index.ts +15 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo, useRef, useCallback } from 'react'
|
|
2
|
-
import { useRefreshAnimation } from '../../hooks/useRefreshAnimation'
|
|
3
2
|
import { useTrafficSources, useTrafficFlows, useTrafficConnect, useSetTrafficSource } from '../../api/traffic'
|
|
4
3
|
import { useClusterInfo } from '../../api/client'
|
|
5
4
|
import type { TrafficWizardState, AggregatedFlow } from '../../types'
|
|
@@ -7,11 +6,12 @@ import { TrafficWizard } from './TrafficWizard'
|
|
|
7
6
|
import { TrafficGraph, type TrafficGraphSelection } from './TrafficGraph'
|
|
8
7
|
import { TrafficFilterSidebar } from './TrafficFilterSidebar'
|
|
9
8
|
import { TrafficFlowListProvider } from './TrafficFlowListContext'
|
|
10
|
-
import { Loader2,
|
|
9
|
+
import { Loader2, Filter, Plug, ChevronDown, List, Activity, AlertTriangle } from 'lucide-react'
|
|
11
10
|
import { clsx } from 'clsx'
|
|
12
11
|
import { useQueryClient } from '@tanstack/react-query'
|
|
13
12
|
import { useDock } from '../dock'
|
|
14
|
-
import { EmptyState, PaneLoader } from '@skyhook-io/k8s-ui'
|
|
13
|
+
import { EmptyState, PaneLoader, FreshnessControl } from '@skyhook-io/k8s-ui'
|
|
14
|
+
import { useConnection } from '../../context/ConnectionContext'
|
|
15
15
|
import { Tooltip } from '../ui/Tooltip'
|
|
16
16
|
|
|
17
17
|
// Addon types for filtering
|
|
@@ -339,6 +339,7 @@ interface TrafficViewProps {
|
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
export function TrafficView({ namespaces }: TrafficViewProps) {
|
|
342
|
+
const { connection } = useConnection()
|
|
342
343
|
const [wizardState, setWizardState] = useState<TrafficWizardState>('detecting')
|
|
343
344
|
const [timeRange, setTimeRange] = useState<string>('5m')
|
|
344
345
|
const [hideSystem, setHideSystem] = useState(true)
|
|
@@ -421,8 +422,8 @@ export function TrafficView({ namespaces }: TrafficViewProps) {
|
|
|
421
422
|
|
|
422
423
|
const {
|
|
423
424
|
data: flowsData,
|
|
424
|
-
isLoading: flowsLoading,
|
|
425
425
|
isFetching: flowsFetching,
|
|
426
|
+
dataUpdatedAt: flowsUpdatedAt,
|
|
426
427
|
refetch: refetchFlowsRaw,
|
|
427
428
|
} = useTrafficFlows({
|
|
428
429
|
namespaces,
|
|
@@ -430,7 +431,6 @@ export function TrafficView({ namespaces }: TrafficViewProps) {
|
|
|
430
431
|
// Only fetch flows when connected (not connecting and no connection error)
|
|
431
432
|
enabled: wizardState === 'ready' && !isConnecting && !connectionError,
|
|
432
433
|
})
|
|
433
|
-
const [refetchFlows, isRefreshAnimating] = useRefreshAnimation(refetchFlowsRaw)
|
|
434
434
|
|
|
435
435
|
// Auto-retry when flows return with warning but no data (e.g., port-forward not ready yet)
|
|
436
436
|
useEffect(() => {
|
|
@@ -1125,12 +1125,19 @@ export function TrafficView({ namespaces }: TrafficViewProps) {
|
|
|
1125
1125
|
</button>
|
|
1126
1126
|
</Tooltip>
|
|
1127
1127
|
)}
|
|
1128
|
-
<div className="flex items-center
|
|
1128
|
+
<div className="flex items-center px-2 py-1 rounded-lg bg-theme-surface/90 backdrop-blur border border-theme-border text-[10px] text-theme-text-tertiary tabular-nums">
|
|
1129
1129
|
{flowStats.shown}/{flowStats.total}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1130
|
+
</div>
|
|
1131
|
+
{/* Flows are a REST snapshot (no poll, no stream), so this is
|
|
1132
|
+
an honest "Updated N ago" + manual refresh — not "live". */}
|
|
1133
|
+
<div className="flex items-center rounded-lg bg-theme-surface/90 backdrop-blur border border-theme-border px-1.5 py-0.5">
|
|
1134
|
+
<FreshnessControl
|
|
1135
|
+
mode="snapshot"
|
|
1136
|
+
dataUpdatedAt={flowsUpdatedAt}
|
|
1137
|
+
isFetching={flowsFetching}
|
|
1138
|
+
onRefresh={() => refetchFlowsRaw()}
|
|
1139
|
+
connectionState={connection.state}
|
|
1140
|
+
/>
|
|
1134
1141
|
</div>
|
|
1135
1142
|
</div>
|
|
1136
1143
|
</>
|
|
@@ -39,10 +39,10 @@ export function Markdown({ children, className }: MarkdownProps) {
|
|
|
39
39
|
</a>
|
|
40
40
|
),
|
|
41
41
|
ul: ({ children }) => (
|
|
42
|
-
<ul className="list-disc list-
|
|
42
|
+
<ul className="list-disc list-outside pl-4 my-2 space-y-1 text-theme-text-secondary">{children}</ul>
|
|
43
43
|
),
|
|
44
44
|
ol: ({ children }) => (
|
|
45
|
-
<ol className="list-decimal list-
|
|
45
|
+
<ol className="list-decimal list-outside pl-4 my-2 space-y-1 text-theme-text-secondary">{children}</ol>
|
|
46
46
|
),
|
|
47
47
|
li: ({ children }) => (
|
|
48
48
|
<li className="leading-relaxed">{children}</li>
|
|
@@ -414,7 +414,7 @@ export const Omnibar = forwardRef<OmnibarHandle, OmnibarProps>(function Omnibar(
|
|
|
414
414
|
return (
|
|
415
415
|
<div
|
|
416
416
|
ref={containerRef}
|
|
417
|
-
className={clsx('relative w-full', hero ? 'max-w-3xl' : 'max-w-
|
|
417
|
+
className={clsx('relative w-full', hero ? 'max-w-3xl' : 'max-w-lg', open && hero && 'z-[16]')}
|
|
418
418
|
// Open on click even when the field is already focused — onFocus alone
|
|
419
419
|
// never fires again, so an autofocused hero (Home) wouldn't reveal the
|
|
420
420
|
// launcher on a click.
|
|
@@ -161,8 +161,12 @@ interface WorkloadViewProps {
|
|
|
161
161
|
onNavigateToResource?: NavigateToResource
|
|
162
162
|
onCollapseToDrawer?: () => void
|
|
163
163
|
expanded?: boolean
|
|
164
|
+
/** false on the outgoing layer during an expand/collapse crossfade (default true) */
|
|
165
|
+
active?: boolean
|
|
164
166
|
onClose?: () => void
|
|
165
|
-
onExpand?: () => void
|
|
167
|
+
onExpand?: (opts?: { yaml?: boolean }) => void
|
|
168
|
+
onExpandIntent?: () => void
|
|
169
|
+
onCancelExpandIntent?: () => void
|
|
166
170
|
initialTab?: 'detail' | 'yaml'
|
|
167
171
|
group?: string
|
|
168
172
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, type ReactNode } from 'react';
|
|
2
|
+
import { useSearchParams } from 'react-router-dom';
|
|
3
|
+
import { FilterLocationProvider, type FilterLocation } from '@skyhook-io/k8s-ui';
|
|
4
|
+
|
|
5
|
+
// Adapts OSS Radar's react-router search params to the app-agnostic
|
|
6
|
+
// FilterLocation seam that @skyhook-io/k8s-ui's useFilterState reads. Mounted
|
|
7
|
+
// once inside the router; every list view's shared filter state flows through
|
|
8
|
+
// it, keeping the URL the single source of truth. (Radar Hub provides its own
|
|
9
|
+
// bridge over its router — k8s-ui itself never depends on react-router.)
|
|
10
|
+
export function FilterLocationBridge({ children }: { children: ReactNode }) {
|
|
11
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
12
|
+
|
|
13
|
+
// React Router's functional updater is NOT state-queued: two updates in one
|
|
14
|
+
// tick can both read the same params and clobber. Advance a ref synchronously
|
|
15
|
+
// so successive filter changes (e.g. toggling two facets fast) compose.
|
|
16
|
+
const latest = useRef(searchParams);
|
|
17
|
+
latest.current = searchParams;
|
|
18
|
+
|
|
19
|
+
const update = useCallback<FilterLocation['update']>(
|
|
20
|
+
(updater, opts) => {
|
|
21
|
+
const next = updater(new URLSearchParams(latest.current));
|
|
22
|
+
latest.current = next;
|
|
23
|
+
setSearchParams(next, opts);
|
|
24
|
+
},
|
|
25
|
+
[setSearchParams],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const value = useMemo<FilterLocation>(() => ({ searchParams, update }), [searchParams, update]);
|
|
29
|
+
return <FilterLocationProvider value={value}>{children}</FilterLocationProvider>;
|
|
30
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,21 @@ export { ShortcutHelpOverlay } from './components/ui/ShortcutHelpOverlay';
|
|
|
23
23
|
export { ClusterSwitcher } from '@skyhook-io/k8s-ui';
|
|
24
24
|
export type { ClusterSwitcherProps, ClusterSwitcherItem } from '@skyhook-io/k8s-ui';
|
|
25
25
|
|
|
26
|
+
// Shared namespace-scope picker primitive — re-exported so embedders (Radar
|
|
27
|
+
// Hub) can render a namespace filter visually identical to OSS Radar's, driving
|
|
28
|
+
// their own per-cluster scope (Hub via ?namespaces= on the embedded RadarApp).
|
|
29
|
+
export { NamespacePicker } from '@skyhook-io/k8s-ui';
|
|
30
|
+
export type {
|
|
31
|
+
NamespacePickerProps,
|
|
32
|
+
NamespacePickerHandle,
|
|
33
|
+
NamespaceScopeView,
|
|
34
|
+
} from '@skyhook-io/k8s-ui';
|
|
35
|
+
|
|
36
|
+
// Shared bordered shell that groups the cluster + namespace segments into one
|
|
37
|
+
// pill — so Radar Hub's cluster top bar matches OSS Radar's header exactly.
|
|
38
|
+
export { ScopePill } from '@skyhook-io/k8s-ui';
|
|
39
|
+
export type { ScopePillProps } from '@skyhook-io/k8s-ui';
|
|
40
|
+
|
|
26
41
|
// Deep-link builders — so consumers (Radar Hub) construct deep links into a
|
|
27
42
|
// cluster view without hand-rolling Radar's internal URL format, which drifts
|
|
28
43
|
// silently when Radar re-routes. `resourcePath` opens the detail drawer for any
|