@skyhook-io/radar-app 0.1.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 +67 -0
- package/package.json +80 -0
- package/src/App.tsx +1538 -0
- package/src/RadarApp.tsx +145 -0
- package/src/api/apiResources.ts +28 -0
- package/src/api/client.ts +2583 -0
- package/src/api/config.ts +116 -0
- package/src/api/traffic.ts +139 -0
- package/src/components/ConnectionErrorView.tsx +272 -0
- package/src/components/ContextSwitcher.tsx +481 -0
- package/src/components/DebugOverlay.tsx +94 -0
- package/src/components/UserMenu.tsx +87 -0
- package/src/components/audit/AuditSettingsDialog.tsx +162 -0
- package/src/components/audit/AuditView.tsx +123 -0
- package/src/components/cost/CostTrendChart.tsx +388 -0
- package/src/components/cost/CostView.tsx +545 -0
- package/src/components/dock/BottomDock.tsx +96 -0
- package/src/components/dock/DockContext.tsx +11 -0
- package/src/components/dock/LocalTerminalTab.tsx +22 -0
- package/src/components/dock/LogsTab.tsx +26 -0
- package/src/components/dock/NodeTerminalTab.tsx +50 -0
- package/src/components/dock/TerminalTab.tsx +42 -0
- package/src/components/dock/TrafficFlowListTab.tsx +18 -0
- package/src/components/dock/WorkloadLogsTab.tsx +23 -0
- package/src/components/dock/index.ts +2 -0
- package/src/components/gitops/GitOpsActions.tsx +1 -0
- package/src/components/gitops/GitOpsStatusBadge.tsx +1 -0
- package/src/components/gitops/ManagedResourcesList.tsx +1 -0
- package/src/components/gitops/SyncCountdown.tsx +1 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/helm/ChartBrowser.tsx +580 -0
- package/src/components/helm/HelmReleaseDrawer.tsx +774 -0
- package/src/components/helm/HelmView.tsx +475 -0
- package/src/components/helm/InstallWizard.tsx +1060 -0
- package/src/components/helm/ManifestDiffViewer.tsx +91 -0
- package/src/components/helm/ManifestViewer.tsx +61 -0
- package/src/components/helm/OwnedResources.tsx +465 -0
- package/src/components/helm/RevisionHistory.tsx +167 -0
- package/src/components/helm/ValuesDiffPreview.tsx +190 -0
- package/src/components/helm/ValuesViewer.tsx +365 -0
- package/src/components/helm/helm-utils.ts +37 -0
- package/src/components/home/ActivitySummary.tsx +262 -0
- package/src/components/home/CertificateHealthCard.tsx +105 -0
- package/src/components/home/ClusterHealthCard.tsx +483 -0
- package/src/components/home/CostCard.tsx +112 -0
- package/src/components/home/HealthRing.tsx +1 -0
- package/src/components/home/HelmSummary.tsx +129 -0
- package/src/components/home/HomeView.tsx +224 -0
- package/src/components/home/MCPSetupDialog.tsx +417 -0
- package/src/components/home/NetworkPolicyCoverageCard.tsx +109 -0
- package/src/components/home/TopologyPreview.tsx +219 -0
- package/src/components/home/TrafficSummary.tsx +154 -0
- package/src/components/logs/JsonLogLine.tsx +1 -0
- package/src/components/logs/LogCore.tsx +2 -0
- package/src/components/logs/LogsViewer.tsx +44 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +40 -0
- package/src/components/logs/useLogBuffer.ts +2 -0
- package/src/components/logs/useLogSearch.ts +1 -0
- package/src/components/portforward/PortForwardButton.tsx +375 -0
- package/src/components/portforward/PortForwardManager.tsx +871 -0
- package/src/components/resource/PrometheusCharts.tsx +687 -0
- package/src/components/resource-drawer/ResourceDrawer.tsx +214 -0
- package/src/components/resources/ImageFilesystemModal.tsx +745 -0
- package/src/components/resources/PodFilesystemModal.tsx +407 -0
- package/src/components/resources/ResourceDetailDrawer.tsx +43 -0
- package/src/components/resources/ResourcesView.tsx +190 -0
- package/src/components/resources/drawer-components.tsx +1 -0
- package/src/components/resources/file-browser-utils.ts +35 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +1 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +17 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +1 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +1 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +1 -0
- package/src/components/resources/renderers/CertificateRenderer.tsx +1 -0
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +1 -0
- package/src/components/resources/renderers/ChallengeRenderer.tsx +1 -0
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +1 -0
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +1 -0
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +1 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +1 -0
- package/src/components/resources/renderers/EventRenderer.tsx +1 -0
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +1 -0
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -0
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +1 -0
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/GatewayRenderer.tsx +1 -0
- package/src/components/resources/renderers/GenericRenderer.tsx +1 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +1 -0
- package/src/components/resources/renderers/HPARenderer.tsx +1 -0
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +1 -0
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +1 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +1 -0
- package/src/components/resources/renderers/JobRenderer.tsx +1 -0
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +1 -0
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +1 -0
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +1 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +1 -0
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +1 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +1 -0
- package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/LeaseRenderer.tsx +1 -0
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +1 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +44 -0
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +1 -0
- package/src/components/resources/renderers/OrderRenderer.tsx +1 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +1 -0
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -0
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -0
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +1 -0
- package/src/components/resources/renderers/PodRenderer.tsx +94 -0
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +1 -0
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +1 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +1 -0
- package/src/components/resources/renderers/RoleRenderer.tsx +1 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +1 -0
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/SbomReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +1 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +1 -0
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +1 -0
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -0
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +1 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +26 -0
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -0
- package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -0
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -0
- package/src/components/resources/renderers/VPARenderer.tsx +1 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +1 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +1 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +1 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +1 -0
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -0
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +1 -0
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +1 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +1 -0
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +1 -0
- package/src/components/resources/renderers/WorkloadRenderer.tsx +52 -0
- package/src/components/resources/renderers/argo-cells.tsx +1 -0
- package/src/components/resources/renderers/certmanager-cells.tsx +1 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +1 -0
- package/src/components/resources/renderers/eso-cells.tsx +1 -0
- package/src/components/resources/renderers/flux-cells.tsx +1 -0
- package/src/components/resources/renderers/index.ts +91 -0
- package/src/components/resources/renderers/istio-cells.tsx +1 -0
- package/src/components/resources/renderers/karpenter-cells.tsx +1 -0
- package/src/components/resources/renderers/keda-cells.tsx +1 -0
- package/src/components/resources/renderers/knative-cells.tsx +1 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +1 -0
- package/src/components/resources/renderers/prometheus-cells.tsx +1 -0
- package/src/components/resources/renderers/traefik-cells.tsx +1 -0
- package/src/components/resources/renderers/trivy-cells.tsx +1 -0
- package/src/components/resources/renderers/trivy-shared.tsx +1 -0
- package/src/components/resources/renderers/velero-cells.tsx +1 -0
- package/src/components/resources/resource-utils-argo.ts +2 -0
- package/src/components/resources/resource-utils-certmanager.ts +2 -0
- package/src/components/resources/resource-utils-cnpg.ts +2 -0
- package/src/components/resources/resource-utils-eso.ts +2 -0
- package/src/components/resources/resource-utils-flux.ts +2 -0
- package/src/components/resources/resource-utils-istio.ts +2 -0
- package/src/components/resources/resource-utils-karpenter.ts +2 -0
- package/src/components/resources/resource-utils-keda.ts +2 -0
- package/src/components/resources/resource-utils-knative.ts +2 -0
- package/src/components/resources/resource-utils-kyverno.ts +2 -0
- package/src/components/resources/resource-utils-prometheus.ts +2 -0
- package/src/components/resources/resource-utils-traefik.ts +1 -0
- package/src/components/resources/resource-utils-trivy.ts +2 -0
- package/src/components/resources/resource-utils-velero.ts +2 -0
- package/src/components/resources/resource-utils.ts +5 -0
- package/src/components/settings/SettingsDialog.tsx +537 -0
- package/src/components/shared/CreateResourceDialog.tsx +17 -0
- package/src/components/shared/EditableYamlView.tsx +24 -0
- package/src/components/shared/LargeClusterNamespacePicker.tsx +70 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +31 -0
- package/src/components/timeline/DiffViewer.tsx +1 -0
- package/src/components/timeline/TimelineList.tsx +69 -0
- package/src/components/timeline/TimelineSwimlanes.tsx +1308 -0
- package/src/components/timeline/TimelineView.tsx +157 -0
- package/src/components/timeline/shared.tsx +1 -0
- package/src/components/traffic/TrafficFilterSidebar.tsx +571 -0
- package/src/components/traffic/TrafficFlowList.tsx +415 -0
- package/src/components/traffic/TrafficFlowListContext.tsx +68 -0
- package/src/components/traffic/TrafficGraph.tsx +1546 -0
- package/src/components/traffic/TrafficView.tsx +1213 -0
- package/src/components/traffic/TrafficWizard.tsx +386 -0
- package/src/components/traffic/index.ts +3 -0
- package/src/components/ui/CodeViewer.tsx +8 -0
- package/src/components/ui/CommandPalette.tsx +460 -0
- package/src/components/ui/ConfirmDialog.tsx +1 -0
- package/src/components/ui/DiagnosticsOverlay.tsx +619 -0
- package/src/components/ui/ErrorBoundary.tsx +46 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +1 -0
- package/src/components/ui/Markdown.tsx +108 -0
- package/src/components/ui/MetricsChart.tsx +1 -0
- package/src/components/ui/NamespaceSelector.tsx +436 -0
- package/src/components/ui/ResourceBar.tsx +1 -0
- package/src/components/ui/ShortcutHelpOverlay.tsx +301 -0
- package/src/components/ui/Toast.tsx +1 -0
- package/src/components/ui/Tooltip.tsx +1 -0
- package/src/components/ui/UpdateNotification.tsx +299 -0
- package/src/components/ui/YamlEditor.tsx +1 -0
- package/src/components/workload/WorkloadView.tsx +532 -0
- package/src/context/ConnectionContext.tsx +173 -0
- package/src/context/ContextSwitchContext.tsx +56 -0
- package/src/context/NavCustomization.tsx +62 -0
- package/src/context/ThemeContext.tsx +97 -0
- package/src/contexts/CapabilitiesContext.tsx +130 -0
- package/src/hooks/useAnimatedUnmount.ts +1 -0
- package/src/hooks/useDesktopDownload.ts +41 -0
- package/src/hooks/useEventSource.ts +262 -0
- package/src/hooks/useFavorites.ts +69 -0
- package/src/hooks/useKeyboardShortcuts.tsx +7 -0
- package/src/hooks/useRefreshAnimation.ts +1 -0
- package/src/index.css +243 -0
- package/src/index.ts +17 -0
- package/src/main.tsx +158 -0
- package/src/types/gitops.ts +2 -0
- package/src/types.ts +3 -0
- package/src/utils/animation.ts +2 -0
- package/src/utils/badge-colors.ts +2 -0
- package/src/utils/context-name.ts +2 -0
- package/src/utils/desktop-download.ts +66 -0
- package/src/utils/desktop-open-folder.ts +21 -0
- package/src/utils/format.ts +2 -0
- package/src/utils/log-format.ts +12 -0
- package/src/utils/navigation.ts +23 -0
- package/src/utils/resource-hierarchy.ts +2 -0
- package/src/utils/resource-icons.ts +2 -0
- package/src/utils/skeleton-yaml.ts +2 -0
- package/src/utils/traffic-colors.ts +54 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { History, Eye, GitCompare, Check, RotateCcw } from 'lucide-react'
|
|
3
|
+
import { clsx } from 'clsx'
|
|
4
|
+
import type { HelmRevision } from '../../types'
|
|
5
|
+
import { getStatusColor, formatDate, formatAge } from './helm-utils'
|
|
6
|
+
import { SEVERITY_BADGE } from '../../utils/badge-colors'
|
|
7
|
+
|
|
8
|
+
interface RevisionHistoryProps {
|
|
9
|
+
history: HelmRevision[]
|
|
10
|
+
currentRevision: number
|
|
11
|
+
onViewRevision: (revision: number) => void
|
|
12
|
+
onCompare: (rev1: number, rev2: number) => void
|
|
13
|
+
onRollback?: (revision: number) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function RevisionHistory({ history, currentRevision, onViewRevision, onCompare, onRollback }: RevisionHistoryProps) {
|
|
17
|
+
const [selectedForCompare, setSelectedForCompare] = useState<number | null>(null)
|
|
18
|
+
|
|
19
|
+
const handleCompareClick = (revision: number) => {
|
|
20
|
+
if (selectedForCompare === null) {
|
|
21
|
+
setSelectedForCompare(revision)
|
|
22
|
+
} else if (selectedForCompare === revision) {
|
|
23
|
+
setSelectedForCompare(null)
|
|
24
|
+
} else {
|
|
25
|
+
// Compare the two revisions (older first)
|
|
26
|
+
const rev1 = Math.min(selectedForCompare, revision)
|
|
27
|
+
const rev2 = Math.max(selectedForCompare, revision)
|
|
28
|
+
onCompare(rev1, rev2)
|
|
29
|
+
setSelectedForCompare(null)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!history || history.length === 0) {
|
|
34
|
+
return (
|
|
35
|
+
<div className="flex flex-col items-center justify-center h-32 text-theme-text-tertiary gap-2">
|
|
36
|
+
<History className="w-8 h-8 text-theme-text-disabled" />
|
|
37
|
+
<span>No revision history</span>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="p-4">
|
|
44
|
+
{/* Compare mode indicator */}
|
|
45
|
+
{selectedForCompare !== null && (
|
|
46
|
+
<div className="mb-4 p-3 bg-blue-500/10 border border-blue-500/30 rounded-lg flex items-center justify-between">
|
|
47
|
+
<div className="flex items-center gap-2 text-sm text-blue-300">
|
|
48
|
+
<GitCompare className="w-4 h-4" />
|
|
49
|
+
<span>Select another revision to compare with revision {selectedForCompare}</span>
|
|
50
|
+
</div>
|
|
51
|
+
<button
|
|
52
|
+
onClick={() => setSelectedForCompare(null)}
|
|
53
|
+
className="text-xs text-theme-text-secondary hover:text-theme-text-primary"
|
|
54
|
+
>
|
|
55
|
+
Cancel
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
{/* History timeline */}
|
|
61
|
+
<div className="space-y-2">
|
|
62
|
+
{history.map((revision, index) => {
|
|
63
|
+
const isCurrent = revision.revision === currentRevision
|
|
64
|
+
const isSelectedForCompare = selectedForCompare === revision.revision
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
key={revision.revision}
|
|
69
|
+
className={clsx(
|
|
70
|
+
'relative bg-theme-elevated/30 rounded-lg p-4 border transition-colors',
|
|
71
|
+
isCurrent
|
|
72
|
+
? 'border-green-500/50'
|
|
73
|
+
: isSelectedForCompare
|
|
74
|
+
? 'border-blue-500/50 bg-blue-500/10'
|
|
75
|
+
: 'border-transparent hover:border-theme-border-light'
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
{/* Timeline connector */}
|
|
79
|
+
{index < history.length - 1 && (
|
|
80
|
+
<div className="absolute left-7 top-14 bottom-0 w-0.5 bg-theme-hover -mb-2" />
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
<div className="flex items-start gap-3">
|
|
84
|
+
{/* Revision circle */}
|
|
85
|
+
<div
|
|
86
|
+
className={clsx(
|
|
87
|
+
'w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold shrink-0 z-10',
|
|
88
|
+
isCurrent
|
|
89
|
+
? 'bg-green-500 text-theme-text-primary'
|
|
90
|
+
: 'bg-theme-hover text-theme-text-secondary'
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
{revision.revision}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Revision details */}
|
|
97
|
+
<div className="flex-1 min-w-0">
|
|
98
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
99
|
+
<span className="text-sm font-medium text-theme-text-primary">{revision.chart}</span>
|
|
100
|
+
<span className={clsx('badge-sm', getStatusColor(revision.status))}>
|
|
101
|
+
{revision.status}
|
|
102
|
+
</span>
|
|
103
|
+
{isCurrent && (
|
|
104
|
+
<span className={clsx('badge-sm', SEVERITY_BADGE.success)}>
|
|
105
|
+
Current
|
|
106
|
+
</span>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div className="flex items-center gap-4 mt-1 text-xs text-theme-text-tertiary">
|
|
111
|
+
<span title={revision.updated}>{formatDate(revision.updated)}</span>
|
|
112
|
+
<span>{formatAge(revision.updated)} ago</span>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{revision.description && (
|
|
116
|
+
<p className="mt-2 text-sm text-theme-text-secondary truncate">{revision.description}</p>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{/* Actions */}
|
|
120
|
+
<div className="flex items-center gap-2 mt-3">
|
|
121
|
+
<button
|
|
122
|
+
onClick={() => onViewRevision(revision.revision)}
|
|
123
|
+
className="flex items-center gap-1 px-2 py-1 text-xs text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded"
|
|
124
|
+
>
|
|
125
|
+
<Eye className="w-3.5 h-3.5" />
|
|
126
|
+
View Manifest
|
|
127
|
+
</button>
|
|
128
|
+
<button
|
|
129
|
+
onClick={() => handleCompareClick(revision.revision)}
|
|
130
|
+
className={clsx(
|
|
131
|
+
'flex items-center gap-1 px-2 py-1 text-xs rounded',
|
|
132
|
+
isSelectedForCompare
|
|
133
|
+
? 'bg-blue-500 text-theme-text-primary'
|
|
134
|
+
: 'text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated'
|
|
135
|
+
)}
|
|
136
|
+
>
|
|
137
|
+
{isSelectedForCompare ? (
|
|
138
|
+
<>
|
|
139
|
+
<Check className="w-3.5 h-3.5" />
|
|
140
|
+
Selected
|
|
141
|
+
</>
|
|
142
|
+
) : (
|
|
143
|
+
<>
|
|
144
|
+
<GitCompare className="w-3.5 h-3.5" />
|
|
145
|
+
Compare
|
|
146
|
+
</>
|
|
147
|
+
)}
|
|
148
|
+
</button>
|
|
149
|
+
{!isCurrent && onRollback && (
|
|
150
|
+
<button
|
|
151
|
+
onClick={() => onRollback(revision.revision)}
|
|
152
|
+
className="flex items-center gap-1 px-2 py-1 text-xs text-amber-400 hover:text-amber-300 hover:bg-amber-500/10 rounded"
|
|
153
|
+
>
|
|
154
|
+
<RotateCcw className="w-3.5 h-3.5" />
|
|
155
|
+
Rollback
|
|
156
|
+
</button>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
})}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
import { X, Play, Loader2, FileText, AlertTriangle } from 'lucide-react'
|
|
3
|
+
import { clsx } from 'clsx'
|
|
4
|
+
import type { ValuesPreviewResponse } from '../../types'
|
|
5
|
+
|
|
6
|
+
interface ValuesDiffPreviewProps {
|
|
7
|
+
previewData: ValuesPreviewResponse
|
|
8
|
+
onClose: () => void
|
|
9
|
+
onApply: () => void
|
|
10
|
+
isApplying: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ValuesDiffPreview({
|
|
14
|
+
previewData,
|
|
15
|
+
onClose,
|
|
16
|
+
onApply,
|
|
17
|
+
isApplying,
|
|
18
|
+
}: ValuesDiffPreviewProps) {
|
|
19
|
+
const dialogRef = useRef<HTMLDivElement>(null)
|
|
20
|
+
|
|
21
|
+
// Handle ESC key
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
24
|
+
if (e.key === 'Escape' && !isApplying) {
|
|
25
|
+
e.stopPropagation()
|
|
26
|
+
onClose()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
document.addEventListener('keydown', handleKeyDown, true)
|
|
30
|
+
return () => document.removeEventListener('keydown', handleKeyDown, true)
|
|
31
|
+
}, [onClose, isApplying])
|
|
32
|
+
|
|
33
|
+
// Focus trap
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (dialogRef.current) {
|
|
36
|
+
dialogRef.current.focus()
|
|
37
|
+
}
|
|
38
|
+
}, [])
|
|
39
|
+
|
|
40
|
+
const hasChanges = previewData.manifestDiff.trim().length > 0 &&
|
|
41
|
+
previewData.manifestDiff.split('\n').some(line => line.startsWith('+') || line.startsWith('-'))
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
45
|
+
{/* Backdrop */}
|
|
46
|
+
<div
|
|
47
|
+
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
|
48
|
+
onClick={isApplying ? undefined : onClose}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
{/* Dialog */}
|
|
52
|
+
<div
|
|
53
|
+
ref={dialogRef}
|
|
54
|
+
tabIndex={-1}
|
|
55
|
+
className="relative dialog max-w-4xl w-full mx-4 max-h-[85vh] flex flex-col outline-none"
|
|
56
|
+
>
|
|
57
|
+
{/* Header */}
|
|
58
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-theme-border shrink-0">
|
|
59
|
+
<div className="flex items-center gap-2">
|
|
60
|
+
<FileText className="w-5 h-5 text-blue-400" />
|
|
61
|
+
<h3 className="text-lg font-semibold text-theme-text-primary">Preview Changes</h3>
|
|
62
|
+
</div>
|
|
63
|
+
<button
|
|
64
|
+
onClick={onClose}
|
|
65
|
+
disabled={isApplying}
|
|
66
|
+
className="p-1.5 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded disabled:opacity-50"
|
|
67
|
+
>
|
|
68
|
+
<X className="w-5 h-5" />
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
{/* Content */}
|
|
73
|
+
<div className="flex-1 overflow-auto p-4">
|
|
74
|
+
{hasChanges ? (
|
|
75
|
+
<div className="space-y-4">
|
|
76
|
+
<p className="text-sm text-theme-text-secondary">
|
|
77
|
+
The following manifest changes will be applied:
|
|
78
|
+
</p>
|
|
79
|
+
<DiffView diff={previewData.manifestDiff} />
|
|
80
|
+
</div>
|
|
81
|
+
) : (
|
|
82
|
+
<div className="flex flex-col items-center justify-center py-12 text-theme-text-tertiary">
|
|
83
|
+
<AlertTriangle className="w-12 h-12 mb-3 text-amber-400/50" />
|
|
84
|
+
<p className="text-lg font-medium text-theme-text-secondary">No Manifest Changes</p>
|
|
85
|
+
<p className="text-sm mt-1">
|
|
86
|
+
The new values will not change the rendered manifest.
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{/* Footer */}
|
|
93
|
+
<div className="flex items-center justify-between px-4 py-3 border-t border-theme-border shrink-0 bg-theme-surface/50">
|
|
94
|
+
<div className="text-xs text-theme-text-tertiary">
|
|
95
|
+
{hasChanges ? (
|
|
96
|
+
<span className="flex items-center gap-1">
|
|
97
|
+
<span className="text-green-400">+</span> additions
|
|
98
|
+
<span className="mx-2">|</span>
|
|
99
|
+
<span className="text-red-400">-</span> deletions
|
|
100
|
+
</span>
|
|
101
|
+
) : (
|
|
102
|
+
<span>Values will be updated without manifest changes</span>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-center gap-3">
|
|
106
|
+
<button
|
|
107
|
+
onClick={onClose}
|
|
108
|
+
disabled={isApplying}
|
|
109
|
+
className="px-4 py-2 text-sm font-medium text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded-lg transition-colors disabled:opacity-50"
|
|
110
|
+
>
|
|
111
|
+
Cancel
|
|
112
|
+
</button>
|
|
113
|
+
<button
|
|
114
|
+
onClick={onApply}
|
|
115
|
+
disabled={isApplying}
|
|
116
|
+
className="flex items-center gap-2 px-4 py-2 text-sm font-medium btn-brand rounded-lg"
|
|
117
|
+
>
|
|
118
|
+
{isApplying ? (
|
|
119
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
120
|
+
) : (
|
|
121
|
+
<Play className="w-4 h-4" />
|
|
122
|
+
)}
|
|
123
|
+
Apply Changes
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface DiffViewProps {
|
|
133
|
+
diff: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function DiffView({ diff }: DiffViewProps) {
|
|
137
|
+
const lines = diff.split('\n')
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div className="rounded-lg border border-theme-border overflow-hidden">
|
|
141
|
+
<div className="bg-theme-elevated/50 px-3 py-2 text-xs font-medium text-theme-text-secondary border-b border-theme-border">
|
|
142
|
+
Manifest Diff
|
|
143
|
+
</div>
|
|
144
|
+
<div className="overflow-auto max-h-[50vh]">
|
|
145
|
+
<pre className="text-xs font-mono p-0 m-0">
|
|
146
|
+
{lines.map((line, index) => (
|
|
147
|
+
<DiffLine key={index} line={line} lineNumber={index + 1} />
|
|
148
|
+
))}
|
|
149
|
+
</pre>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface DiffLineProps {
|
|
156
|
+
line: string
|
|
157
|
+
lineNumber: number
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function DiffLine({ line, lineNumber }: DiffLineProps) {
|
|
161
|
+
const isAddition = line.startsWith('+') && !line.startsWith('+++')
|
|
162
|
+
const isDeletion = line.startsWith('-') && !line.startsWith('---')
|
|
163
|
+
const isHeader = line.startsWith('@@') || line.startsWith('---') || line.startsWith('+++')
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div
|
|
167
|
+
className={clsx(
|
|
168
|
+
'flex',
|
|
169
|
+
isAddition && 'bg-green-500/10',
|
|
170
|
+
isDeletion && 'bg-red-500/10',
|
|
171
|
+
isHeader && 'bg-blue-500/10'
|
|
172
|
+
)}
|
|
173
|
+
>
|
|
174
|
+
<span className="w-12 shrink-0 text-right pr-3 py-0.5 text-theme-text-disabled select-none border-r border-theme-border/50">
|
|
175
|
+
{lineNumber}
|
|
176
|
+
</span>
|
|
177
|
+
<span
|
|
178
|
+
className={clsx(
|
|
179
|
+
'flex-1 px-3 py-0.5 whitespace-pre',
|
|
180
|
+
isAddition && 'text-green-400',
|
|
181
|
+
isDeletion && 'text-red-400',
|
|
182
|
+
isHeader && 'text-blue-400 font-medium',
|
|
183
|
+
!isAddition && !isDeletion && !isHeader && 'text-theme-text-secondary'
|
|
184
|
+
)}
|
|
185
|
+
>
|
|
186
|
+
{line}
|
|
187
|
+
</span>
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
}
|