@skyhook-io/radar-app 0.2.2 → 0.3.1
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/README.md +7 -1
- package/package.json +33 -25
- package/src/App.tsx +1449 -382
- package/src/RadarApp.tsx +132 -19
- package/src/api/apiResources.ts +1 -1
- package/src/api/client.argoResourceSync.test.ts +69 -0
- package/src/api/client.delta.test.ts +89 -0
- package/src/api/client.deltaSync.test.ts +216 -0
- package/src/api/client.metrics.test.ts +106 -0
- package/src/api/client.rightsizing.test.ts +32 -0
- package/src/api/client.ts +2730 -271
- package/src/api/client.yaml.test.ts +45 -0
- package/src/api/diagnose.ts +289 -0
- package/src/api/quotas.ts +16 -0
- package/src/api/rbac.ts +57 -0
- package/src/api/timelineSource.test.ts +217 -0
- package/src/api/timelineSource.ts +582 -0
- package/src/components/ConnectionErrorView.tsx +186 -70
- package/src/components/ContextSwitcher.tsx +63 -18
- package/src/components/DebugOverlay.tsx +5 -3
- package/src/components/NamespaceSwitcher.tsx +41 -0
- package/src/components/UserMenu.tsx +69 -21
- package/src/components/applications/ApplicationsView.tsx +936 -0
- package/src/components/audit/AuditSettingsDialog.tsx +79 -17
- package/src/components/audit/AuditView.tsx +65 -62
- package/src/components/compare/CompareViewRoute.tsx +124 -0
- package/src/components/compare/useCompareCandidates.ts +27 -0
- package/src/components/compare/useCompareLauncher.tsx +79 -0
- package/src/components/cost/ApplicationCostTab.test.ts +204 -0
- package/src/components/cost/ApplicationCostTab.tsx +571 -0
- package/src/components/cost/CostTrendChart.tsx +106 -75
- package/src/components/cost/CostView.test.ts +12 -0
- package/src/components/cost/CostView.tsx +507 -223
- package/src/components/cost/CostViewTabs.test.tsx +21 -0
- package/src/components/cost/CostViewTabs.tsx +40 -0
- package/src/components/cost/CurrentAllocationUse.test.ts +21 -0
- package/src/components/cost/CurrentAllocationUse.tsx +126 -0
- package/src/components/cost/WorkloadCostTab.test.ts +153 -0
- package/src/components/cost/WorkloadCostTab.tsx +372 -0
- package/src/components/cost/cloud-console.test.ts +39 -0
- package/src/components/cost/cloud-console.ts +81 -0
- package/src/components/cost/errors.ts +8 -0
- package/src/components/cost/format.test.ts +27 -0
- package/src/components/cost/format.ts +46 -0
- package/src/components/cost/kinds.ts +5 -0
- package/src/components/curl/ServiceCurlButton.tsx +445 -0
- package/src/components/diagnose/AISettings.tsx +147 -0
- package/src/components/diagnose/DiagnoseContext.tsx +495 -0
- package/src/components/diagnose/DiagnoseSurface.tsx +394 -0
- package/src/components/diagnose/Home.tsx +163 -0
- package/src/components/diagnose/InvestigationView.tsx +622 -0
- package/src/components/diagnose/LocalDiagnoseAction.tsx +162 -0
- package/src/components/diagnose/launch.ts +65 -0
- package/src/components/diagnose/parts.tsx +1756 -0
- package/src/components/dock/BottomDock.tsx +2 -3
- package/src/components/dock/DockContext.tsx +1 -0
- package/src/components/dock/TerminalTab.tsx +1 -1
- package/src/components/dock/WorkloadLogsTab.tsx +21 -5
- package/src/components/dock/index.ts +1 -1
- package/src/components/execution/BatchExecutionView.test.ts +170 -0
- package/src/components/execution/BatchExecutionView.tsx +1329 -0
- package/src/components/execution/batch-run-actions.test.ts +48 -0
- package/src/components/execution/batch-run-actions.ts +24 -0
- package/src/components/execution/batch-timeline.test.ts +57 -0
- package/src/components/execution/batch-timeline.ts +46 -0
- package/src/components/execution/execution-definition.test.ts +208 -0
- package/src/components/execution/execution-definition.ts +245 -0
- package/src/components/gitops/ArgoResourceDiffLoader.tsx +23 -0
- package/src/components/gitops/GitOpsView.tsx +1042 -0
- package/src/components/gitops/RevisionMetaChip.tsx +63 -0
- package/src/components/helm/ChartBrowser.tsx +87 -31
- package/src/components/helm/HelmCompareRoute.tsx +1341 -0
- package/src/components/helm/HelmReleaseDrawer.test.ts +17 -0
- package/src/components/helm/HelmReleaseDrawer.tsx +1073 -102
- package/src/components/helm/HelmView.tsx +237 -96
- package/src/components/helm/InstallWizard.tsx +94 -38
- package/src/components/helm/ManifestDiffViewer.tsx +8 -27
- package/src/components/helm/OwnedResources.tsx +34 -59
- package/src/components/helm/RevisionHistory.tsx +52 -3
- package/src/components/helm/RoleGatedPanel.tsx +3 -3
- package/src/components/helm/TrackChartSourceDialog.tsx +185 -0
- package/src/components/helm/ValuesDiffPreview.tsx +17 -7
- package/src/components/helm/ValuesViewer.tsx +49 -53
- package/src/components/helm/helm-utils.ts +4 -0
- package/src/components/home/ActivitySummary.tsx +4 -1
- package/src/components/home/ClusterHealthCard.tsx +56 -42
- package/src/components/home/CostCard.tsx +21 -36
- package/src/components/home/GitOpsControllersCard.tsx +110 -0
- package/src/components/home/HelmSummary.tsx +3 -1
- package/src/components/home/HomeView.tsx +339 -105
- package/src/components/home/MCPSetupDialog.tsx +29 -87
- package/src/components/home/TrafficSummary.tsx +2 -2
- package/src/components/home/mcpToolCatalog.ts +333 -0
- package/src/components/issues/IssuesPane.tsx +151 -0
- package/src/components/logs/LogsViewer.tsx +4 -1
- package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +135 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +4 -1
- package/src/components/nav/PrimaryNavRail.tsx +285 -0
- package/src/components/portforward/PortForwardButton.tsx +118 -47
- package/src/components/portforward/PortForwardManager.tsx +253 -131
- package/src/components/resource/HPACharts.tsx +237 -0
- package/src/components/resource/PVCUsageBar.tsx +59 -0
- package/src/components/resource/PrometheusCharts.tsx +160 -584
- package/src/components/resource/PrometheusChartsGrid.tsx +270 -0
- package/src/components/resource/RestartChart.tsx +133 -0
- package/src/components/resource/RightsizingStrip.test.ts +109 -0
- package/src/components/resource/RightsizingStrip.tsx +363 -0
- package/src/components/resource-drawer/ResourceDrawer.tsx +3 -1
- package/src/components/resources/CompositeRenderer.tsx +101 -0
- package/src/components/resources/ImageFilesystemModal.tsx +19 -12
- package/src/components/resources/PodFilesystemModal.tsx +6 -5
- package/src/components/resources/ResourceDetailDrawer.tsx +13 -3
- package/src/components/resources/ResourcesView.tsx +194 -17
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +1 -0
- package/src/components/resources/renderers/HPARenderer.tsx +20 -1
- package/src/components/resources/renderers/NamespaceRenderer.tsx +31 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +10 -4
- package/src/components/resources/renderers/PVCRenderer.tsx +19 -1
- package/src/components/resources/renderers/PodRenderer.tsx +30 -6
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +45 -1
- package/src/components/resources/renderers/RoleRenderer.tsx +27 -1
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +28 -1
- package/src/components/resources/renderers/ServiceRenderer.tsx +81 -8
- package/src/components/resources/renderers/WorkloadRenderer.tsx +51 -4
- package/src/components/resources/renderers/index.ts +2 -0
- package/src/components/resources/resource-utils.ts +2 -1
- package/src/components/rightsizing/RightsizingScanView.tsx +938 -0
- package/src/components/rightsizing/copy.test.ts +56 -0
- package/src/components/rightsizing/model.test.ts +227 -0
- package/src/components/rightsizing/model.ts +158 -0
- package/src/components/rightsizing/presentation.test.ts +104 -0
- package/src/components/rightsizing/presentation.ts +94 -0
- package/src/components/settings/MyPermissionsDialog.tsx +241 -0
- package/src/components/settings/SettingsDialog.tsx +1505 -165
- package/src/components/shared/CreateResourceDialog.tsx +9 -2
- package/src/components/shared/LargeClusterNamespacePicker.tsx +3 -3
- package/src/components/timeline/LocalTimelineScrubber.tsx +212 -0
- package/src/components/timeline/RetainedTimelineScrubber.tsx +311 -0
- package/src/components/timeline/TimelineList.tsx +86 -13
- package/src/components/timeline/TimelineSwimlanes.tsx +9 -1299
- package/src/components/timeline/TimelineView.tsx +873 -24
- package/src/components/timeline/TimelineView.urlparams.test.ts +335 -0
- package/src/components/traffic/TrafficFilterSidebar.tsx +10 -45
- package/src/components/traffic/TrafficFlowList.tsx +29 -15
- package/src/components/traffic/TrafficGraph.tsx +42 -24
- package/src/components/traffic/TrafficView.tsx +32 -19
- package/src/components/ui/CommandPalette.tsx +8 -215
- package/src/components/ui/DiagnosticsOverlay.tsx +219 -9
- package/src/components/ui/Markdown.tsx +3 -3
- package/src/components/ui/Omnibar.tsx +602 -0
- package/src/components/ui/RadarOmnibar.tsx +52 -0
- package/src/components/ui/SearchSyntaxHelp.tsx +89 -0
- package/src/components/ui/ShortcutHelpOverlay.tsx +3 -2
- package/src/components/ui/UpdateNotification.tsx +48 -36
- package/src/components/ui/command-items.ts +178 -0
- package/src/components/workload/WorkloadView.tsx +1342 -158
- package/src/context/ConnectionContext.tsx +146 -21
- package/src/context/DiagnoseCustomization.tsx +93 -0
- package/src/context/NavCustomization.tsx +75 -0
- package/src/context/TimelineSource.tsx +50 -0
- package/src/contexts/CapabilitiesContext.tsx +32 -8
- package/src/filter/FilterLocationBridge.tsx +30 -0
- package/src/hooks/useClusterLoadState.ts +73 -0
- package/src/hooks/useDocumentTitle.ts +25 -0
- package/src/hooks/useEventSource.ts +6 -0
- package/src/hooks/useKeyboardShortcuts.tsx +1 -0
- package/src/hooks/useMediaQuery.ts +21 -0
- package/src/hooks/useNavRailPinned.ts +46 -0
- package/src/hooks/useRecentResources.ts +49 -0
- package/src/index.css +162 -1
- package/src/index.ts +73 -1
- package/src/main.tsx +7 -5
- package/src/types/clusterLoadState.ts +33 -0
- package/src/types.ts +2 -0
- package/src/utils/auditBadges.ts +53 -0
- package/src/utils/navigation.ts +64 -1
- package/src/components/ui/NamespaceSelector.tsx +0 -436
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ShieldCheck, ShieldAlert } from 'lucide-react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { useArgoRevisionMetadata } from '../../api/client'
|
|
4
|
+
import { Tooltip } from '../ui/Tooltip'
|
|
5
|
+
|
|
6
|
+
// Argo returns the author as "Name <email>"; show just the name.
|
|
7
|
+
function authorName(author?: string): string {
|
|
8
|
+
if (!author) return ''
|
|
9
|
+
const lt = author.indexOf('<')
|
|
10
|
+
return (lt >= 0 ? author.slice(0, lt) : author).trim()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// signatureInfo is a raw GPG verification line. "Good signature" → verified;
|
|
14
|
+
// any other non-empty value → a check ran but didn't verify; empty → unsigned.
|
|
15
|
+
function signatureState(sig?: string): 'good' | 'bad' | null {
|
|
16
|
+
if (!sig) return null
|
|
17
|
+
return /good signature/i.test(sig) ? 'good' : 'bad'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// RevisionMetaChip hydrates the Argo status-strip revision with Git commit
|
|
21
|
+
// detail (author, subject, signature) fetched from the Argo CD API. Renders
|
|
22
|
+
// nothing until data arrives, so the bare SHA is never blocked; the whole thing
|
|
23
|
+
// is gated by the host on capabilities.revisionMetadataAvailable.
|
|
24
|
+
export function RevisionMetaChip({
|
|
25
|
+
appNamespace,
|
|
26
|
+
appName,
|
|
27
|
+
revision,
|
|
28
|
+
}: {
|
|
29
|
+
appNamespace: string
|
|
30
|
+
appName: string
|
|
31
|
+
revision: string
|
|
32
|
+
}): ReactNode {
|
|
33
|
+
// The host only renders this chip when revision metadata is available, so no
|
|
34
|
+
// extra enabled gate is needed — the hook's own appName/revision guard suffices.
|
|
35
|
+
const { data } = useArgoRevisionMetadata(appNamespace, appName, revision)
|
|
36
|
+
if (!data) return null
|
|
37
|
+
|
|
38
|
+
const author = authorName(data.author)
|
|
39
|
+
const sig = signatureState(data.signatureInfo)
|
|
40
|
+
const subject = data.message?.split('\n')[0]?.trim()
|
|
41
|
+
if (!author && !sig && !subject) return null
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
{author && <span className="shrink-0 text-theme-text-secondary">· {author}</span>}
|
|
46
|
+
{subject && (
|
|
47
|
+
<Tooltip content={data.message ?? subject} delay={300} wrapperClassName="min-w-0">
|
|
48
|
+
<span className="max-w-[32ch] truncate text-theme-text-tertiary">“{subject}”</span>
|
|
49
|
+
</Tooltip>
|
|
50
|
+
)}
|
|
51
|
+
{sig === 'good' && (
|
|
52
|
+
<Tooltip content={data.signatureInfo ?? 'Signed commit'} delay={300} wrapperClassName="inline-flex shrink-0">
|
|
53
|
+
<ShieldCheck className="h-3 w-3 text-green-600 dark:text-green-400/80" />
|
|
54
|
+
</Tooltip>
|
|
55
|
+
)}
|
|
56
|
+
{sig === 'bad' && (
|
|
57
|
+
<Tooltip content={data.signatureInfo ?? 'Signature not verified'} delay={300} wrapperClassName="inline-flex shrink-0">
|
|
58
|
+
<ShieldAlert className="h-3 w-3 text-amber-600 dark:text-amber-400/80" />
|
|
59
|
+
</Tooltip>
|
|
60
|
+
)}
|
|
61
|
+
</>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { useState, useMemo } from 'react'
|
|
2
2
|
import { Search, RefreshCw, Package, Database, AlertCircle, ExternalLink, ChevronDown, Star, Shield, BadgeCheck, Building2, Globe, ArrowUpDown, FileJson, PenTool } from 'lucide-react'
|
|
3
|
-
import { PaneLoader } from '@skyhook-io/k8s-ui'
|
|
3
|
+
import { PaneLoader, Input } from '@skyhook-io/k8s-ui'
|
|
4
4
|
import { clsx } from 'clsx'
|
|
5
|
-
import { useHelmRepositories, useSearchCharts, useUpdateRepository, useArtifactHubSearch, type ArtifactHubSortOption } from '../../api/client'
|
|
5
|
+
import { useHelmRepositories, useSearchCharts, useUpdateRepository, useUpdateRepositorySilent, useArtifactHubSearch, type ArtifactHubSortOption } from '../../api/client'
|
|
6
6
|
import { useCanHelmWrite } from '../../contexts/CapabilitiesContext'
|
|
7
7
|
import type { ChartInfo, HelmRepository, ArtifactHubChart, ChartSource } from '../../types'
|
|
8
8
|
import { formatAge } from './helm-utils'
|
|
9
9
|
import { SEVERITY_BADGE } from '../../utils/badge-colors'
|
|
10
10
|
import { Tooltip } from '../ui/Tooltip'
|
|
11
|
+
import { useToast } from '../ui/Toast'
|
|
11
12
|
|
|
12
13
|
interface ChartBrowserProps {
|
|
13
14
|
onChartSelect: (repo: string, chart: string, version: string, source: ChartSource) => void
|
|
@@ -26,7 +27,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
26
27
|
|
|
27
28
|
// Repo refresh is gated only by `requireHelmWrite` on the backend
|
|
28
29
|
// (handleUpdateRepository deliberately skips requireCloudRole — it
|
|
29
|
-
// mutates pod-local chart cache, not cluster state). So the
|
|
30
|
+
// mutates pod-local chart cache, not cluster state). So the frontend gate
|
|
30
31
|
// here must NOT include the Cloud role check, or Cloud viewers with
|
|
31
32
|
// rbac.helm=true would be blocked from a refresh the backend allows.
|
|
32
33
|
const canHelmWrite = useCanHelmWrite()
|
|
@@ -66,21 +67,54 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
66
67
|
return groups
|
|
67
68
|
}, [filteredLocalCharts])
|
|
68
69
|
|
|
70
|
+
// Silent variant for the bulk path so the global MutationCache
|
|
71
|
+
// doesn't fire a per-call "Failed to update repository" toast
|
|
72
|
+
// — handleUpdateAllRepos surfaces a single aggregate toast
|
|
73
|
+
// that names the failed repos.
|
|
74
|
+
const updateRepoSilentMutation = useUpdateRepositorySilent()
|
|
75
|
+
const { showError, showSuccess } = useToast()
|
|
76
|
+
// updateRepoSilentMutation.isPending flips to false BETWEEN
|
|
77
|
+
// sequential mutateAsync calls, briefly re-enabling the bulk
|
|
78
|
+
// button mid-loop. Track the whole-batch state explicitly so
|
|
79
|
+
// the user can't kick off a second concurrent batch.
|
|
80
|
+
const [isBatchUpdating, setIsBatchUpdating] = useState(false)
|
|
81
|
+
|
|
69
82
|
const handleUpdateRepo = async (repoName: string) => {
|
|
70
83
|
await updateRepoMutation.mutateAsync(repoName)
|
|
71
84
|
refetchCharts()
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
const handleUpdateAllRepos = async () => {
|
|
75
|
-
if (!repositories) return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
if (!repositories || repositories.length === 0 || isBatchUpdating) return
|
|
89
|
+
setIsBatchUpdating(true)
|
|
90
|
+
const failed: string[] = []
|
|
91
|
+
try {
|
|
92
|
+
for (const repo of repositories) {
|
|
93
|
+
try {
|
|
94
|
+
await updateRepoSilentMutation.mutateAsync(repo.name)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
failed.push(repo.name)
|
|
97
|
+
console.warn(`helm repo update failed for "${repo.name}":`, err)
|
|
98
|
+
}
|
|
81
99
|
}
|
|
100
|
+
} finally {
|
|
101
|
+
setIsBatchUpdating(false)
|
|
82
102
|
}
|
|
83
103
|
refetchCharts()
|
|
104
|
+
const ok = repositories.length - failed.length
|
|
105
|
+
if (failed.length === 0) {
|
|
106
|
+
showSuccess(`Updated ${ok} ${ok === 1 ? 'repository' : 'repositories'}`)
|
|
107
|
+
} else if (ok === 0) {
|
|
108
|
+
showError(
|
|
109
|
+
`Failed to update ${failed.length} ${failed.length === 1 ? 'repository' : 'repositories'}`,
|
|
110
|
+
`Failed: ${failed.join(', ')}`,
|
|
111
|
+
)
|
|
112
|
+
} else {
|
|
113
|
+
showError(
|
|
114
|
+
`Updated ${ok}/${repositories.length} repositories`,
|
|
115
|
+
`Failed: ${failed.join(', ')}`,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
84
118
|
}
|
|
85
119
|
|
|
86
120
|
const isLoading = chartSource === 'local' ? chartsLoading : artifactHubLoading
|
|
@@ -148,7 +182,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
148
182
|
onClick={() => { setSelectedRepo('all'); setRepoDropdownOpen(false) }}
|
|
149
183
|
className={clsx(
|
|
150
184
|
'w-full px-3 py-2 text-left text-sm hover:bg-theme-hover flex items-center justify-between',
|
|
151
|
-
selectedRepo === 'all' ? 'text-
|
|
185
|
+
selectedRepo === 'all' ? 'text-accent' : 'text-theme-text-primary'
|
|
152
186
|
)}
|
|
153
187
|
>
|
|
154
188
|
<span>All Repositories</span>
|
|
@@ -156,7 +190,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
156
190
|
</button>
|
|
157
191
|
<div className="border-t border-theme-border my-1" />
|
|
158
192
|
{reposLoading ? (
|
|
159
|
-
<div className="px-3 py-2 text-sm text-theme-text-tertiary">Loading
|
|
193
|
+
<div className="px-3 py-2 text-sm text-theme-text-tertiary">Loading…</div>
|
|
160
194
|
) : repositories?.length === 0 ? (
|
|
161
195
|
<div className="px-3 py-2 text-sm text-theme-text-tertiary">No repositories configured</div>
|
|
162
196
|
) : (
|
|
@@ -186,9 +220,9 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
186
220
|
type="checkbox"
|
|
187
221
|
checked={showOfficialOnly}
|
|
188
222
|
onChange={(e) => setShowOfficialOnly(e.target.checked)}
|
|
189
|
-
className="rounded border-theme-border text-
|
|
223
|
+
className="rounded border-theme-border text-accent focus:ring-accent"
|
|
190
224
|
/>
|
|
191
|
-
<BadgeCheck className="w-3.5 h-3.5 text-
|
|
225
|
+
<BadgeCheck className="w-3.5 h-3.5 text-accent" />
|
|
192
226
|
Official
|
|
193
227
|
</label>
|
|
194
228
|
<label className="flex items-center gap-1.5 text-sm text-theme-text-secondary">
|
|
@@ -196,7 +230,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
196
230
|
type="checkbox"
|
|
197
231
|
checked={showVerifiedOnly}
|
|
198
232
|
onChange={(e) => setShowVerifiedOnly(e.target.checked)}
|
|
199
|
-
className="rounded border-theme-border text-
|
|
233
|
+
className="rounded border-theme-border text-accent focus:ring-accent"
|
|
200
234
|
/>
|
|
201
235
|
<Shield className="w-3.5 h-3.5 text-green-400" />
|
|
202
236
|
Verified
|
|
@@ -207,7 +241,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
207
241
|
<select
|
|
208
242
|
value={artifactHubSort}
|
|
209
243
|
onChange={(e) => setArtifactHubSort(e.target.value as ArtifactHubSortOption)}
|
|
210
|
-
className="bg-theme-elevated border border-theme-border-light rounded px-2 py-1 text-sm text-theme-text-primary focus:outline-none focus:ring-2 focus:ring-
|
|
244
|
+
className="bg-theme-elevated border border-theme-border-light rounded px-2 py-1 text-sm text-theme-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
|
|
211
245
|
>
|
|
212
246
|
<option value="relevance">Relevance</option>
|
|
213
247
|
<option value="stars">Stars</option>
|
|
@@ -220,12 +254,11 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
220
254
|
{/* Search */}
|
|
221
255
|
<div className="flex-1 relative">
|
|
222
256
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-theme-text-tertiary" />
|
|
223
|
-
<
|
|
224
|
-
type="text"
|
|
257
|
+
<Input
|
|
225
258
|
placeholder={chartSource === 'local' ? "Search charts..." : "Search ArtifactHub..."}
|
|
226
259
|
value={searchTerm}
|
|
227
260
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
228
|
-
className="w-full max-w-md pl-10 pr-4 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-
|
|
261
|
+
className="w-full max-w-md pl-10 pr-4 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-accent"
|
|
229
262
|
/>
|
|
230
263
|
</div>
|
|
231
264
|
|
|
@@ -237,7 +270,7 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
237
270
|
type="checkbox"
|
|
238
271
|
checked={showAllVersions}
|
|
239
272
|
onChange={(e) => setShowAllVersions(e.target.checked)}
|
|
240
|
-
className="rounded border-theme-border text-
|
|
273
|
+
className="rounded border-theme-border text-accent focus:ring-accent"
|
|
241
274
|
/>
|
|
242
275
|
All versions
|
|
243
276
|
</label>
|
|
@@ -246,10 +279,10 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
246
279
|
<Tooltip content={canHelmWrite ? "Update all repositories" : helmWriteReason}>
|
|
247
280
|
<button
|
|
248
281
|
onClick={handleUpdateAllRepos}
|
|
249
|
-
disabled={
|
|
282
|
+
disabled={isBatchUpdating || !canHelmWrite}
|
|
250
283
|
className="p-2 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded-lg disabled:opacity-50"
|
|
251
284
|
>
|
|
252
|
-
<RefreshCw className={clsx('w-4 h-4',
|
|
285
|
+
<RefreshCw className={clsx('w-4 h-4', isBatchUpdating && 'animate-spin')} />
|
|
253
286
|
</button>
|
|
254
287
|
</Tooltip>
|
|
255
288
|
</>
|
|
@@ -273,14 +306,32 @@ export function ChartBrowser({ onChartSelect }: ChartBrowserProps) {
|
|
|
273
306
|
<div className="text-sm mt-1">
|
|
274
307
|
<p>No Helm repositories configured.</p>
|
|
275
308
|
<p className="mt-1">
|
|
276
|
-
Add repositories using <code className="
|
|
309
|
+
Add repositories using <code className="inline-code">helm repo add</code>
|
|
277
310
|
</p>
|
|
278
311
|
<p className="mt-2">
|
|
279
|
-
Or try searching on <button onClick={() => setChartSource('artifacthub')} className="text-
|
|
312
|
+
Or try searching on <button onClick={() => setChartSource('artifacthub')} className="text-accent-text hover:underline">ArtifactHub</button>
|
|
280
313
|
</p>
|
|
281
314
|
</div>
|
|
282
315
|
) : (
|
|
283
|
-
<
|
|
316
|
+
<div className="text-sm mt-1 flex flex-col items-center gap-2">
|
|
317
|
+
<p>Your repositories may be out of date.</p>
|
|
318
|
+
<Tooltip content={canHelmWrite ? 'Run helm repo update on every configured repository' : helmWriteReason}>
|
|
319
|
+
<button
|
|
320
|
+
onClick={handleUpdateAllRepos}
|
|
321
|
+
disabled={isBatchUpdating || !canHelmWrite}
|
|
322
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs btn-brand rounded disabled:opacity-50 disabled:pointer-events-none"
|
|
323
|
+
>
|
|
324
|
+
<RefreshCw className={`w-3.5 h-3.5 ${isBatchUpdating ? 'animate-spin' : ''}`} />
|
|
325
|
+
{isBatchUpdating ? 'Updating…' : 'Update all repositories'}
|
|
326
|
+
</button>
|
|
327
|
+
</Tooltip>
|
|
328
|
+
<button
|
|
329
|
+
onClick={() => setChartSource('artifacthub')}
|
|
330
|
+
className="text-xs text-blue-400 hover:underline"
|
|
331
|
+
>
|
|
332
|
+
Or browse ArtifactHub instead
|
|
333
|
+
</button>
|
|
334
|
+
</div>
|
|
284
335
|
)}
|
|
285
336
|
</div>
|
|
286
337
|
</div>
|
|
@@ -375,7 +426,7 @@ function RepoDropdownItem({ repo, isSelected, onSelect, onUpdate, isUpdating, ca
|
|
|
375
426
|
onClick={onSelect}
|
|
376
427
|
className={clsx(
|
|
377
428
|
'flex-1 text-left text-sm truncate',
|
|
378
|
-
isSelected ? 'text-
|
|
429
|
+
isSelected ? 'text-accent' : 'text-theme-text-primary'
|
|
379
430
|
)}
|
|
380
431
|
>
|
|
381
432
|
{repo.name}
|
|
@@ -385,14 +436,15 @@ function RepoDropdownItem({ repo, isSelected, onSelect, onUpdate, isUpdating, ca
|
|
|
385
436
|
</span>
|
|
386
437
|
)}
|
|
387
438
|
</button>
|
|
439
|
+
<Tooltip content={canUpdate ? "Update repository" : (cantUpdateReason ?? "Helm write permissions required")}>
|
|
388
440
|
<button
|
|
389
441
|
onClick={(e) => { e.stopPropagation(); onUpdate() }}
|
|
390
442
|
disabled={isUpdating || !canUpdate}
|
|
391
|
-
className="p-1 text-theme-text-tertiary hover:text-theme-text-primary opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-50"
|
|
392
|
-
title={canUpdate ? "Update repository" : (cantUpdateReason ?? "Helm write permissions required")}
|
|
443
|
+
className="p-1 text-theme-text-tertiary hover:text-theme-text-primary opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-50 disabled:pointer-events-none"
|
|
393
444
|
>
|
|
394
445
|
<RefreshCw className={clsx('w-3.5 h-3.5', isUpdating && 'animate-spin')} />
|
|
395
446
|
</button>
|
|
447
|
+
</Tooltip>
|
|
396
448
|
</div>
|
|
397
449
|
)
|
|
398
450
|
}
|
|
@@ -413,7 +465,7 @@ function LocalChartCard({ chart, onSelect }: LocalChartCardProps) {
|
|
|
413
465
|
<img
|
|
414
466
|
src={chart.icon}
|
|
415
467
|
alt=""
|
|
416
|
-
className="w-10 h-10 rounded object-contain bg-
|
|
468
|
+
className="w-10 h-10 rounded object-contain bg-theme-elevated p-1"
|
|
417
469
|
onError={(e) => {
|
|
418
470
|
(e.target as HTMLImageElement).style.display = 'none'
|
|
419
471
|
}}
|
|
@@ -425,7 +477,9 @@ function LocalChartCard({ chart, onSelect }: LocalChartCardProps) {
|
|
|
425
477
|
)}
|
|
426
478
|
<div className="flex-1 min-w-0">
|
|
427
479
|
<div className="flex items-center gap-2">
|
|
428
|
-
<
|
|
480
|
+
<Tooltip content={chart.name} wrapperClassName="min-w-0 flex-1">
|
|
481
|
+
<h4 className="text-sm font-medium text-theme-text-primary truncate">{chart.name}</h4>
|
|
482
|
+
</Tooltip>
|
|
429
483
|
{chart.deprecated && (
|
|
430
484
|
<span className={clsx('px-1 py-0.5 text-[10px] rounded', SEVERITY_BADGE.warning)}>
|
|
431
485
|
deprecated
|
|
@@ -476,7 +530,7 @@ function ArtifactHubChartCard({ chart, onSelect }: ArtifactHubChartCardProps) {
|
|
|
476
530
|
<img
|
|
477
531
|
src={chart.logoUrl}
|
|
478
532
|
alt=""
|
|
479
|
-
className="w-12 h-12 rounded object-contain bg-
|
|
533
|
+
className="w-12 h-12 rounded object-contain bg-theme-elevated p-1 shrink-0"
|
|
480
534
|
onError={(e) => {
|
|
481
535
|
(e.target as HTMLImageElement).style.display = 'none'
|
|
482
536
|
}}
|
|
@@ -489,7 +543,9 @@ function ArtifactHubChartCard({ chart, onSelect }: ArtifactHubChartCardProps) {
|
|
|
489
543
|
|
|
490
544
|
{/* Name and org */}
|
|
491
545
|
<div className="flex-1 min-w-0">
|
|
492
|
-
<
|
|
546
|
+
<Tooltip content={chart.name} wrapperClassName="w-full">
|
|
547
|
+
<h4 className="text-sm font-medium text-theme-text-primary truncate">{chart.name}</h4>
|
|
548
|
+
</Tooltip>
|
|
493
549
|
<div className="flex items-center gap-2 mt-0.5 text-xs text-theme-text-tertiary">
|
|
494
550
|
<span className="flex items-center gap-1">
|
|
495
551
|
<Building2 className="w-3 h-3" />
|