@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.
Files changed (251) hide show
  1. package/README.md +67 -0
  2. package/package.json +80 -0
  3. package/src/App.tsx +1538 -0
  4. package/src/RadarApp.tsx +145 -0
  5. package/src/api/apiResources.ts +28 -0
  6. package/src/api/client.ts +2583 -0
  7. package/src/api/config.ts +116 -0
  8. package/src/api/traffic.ts +139 -0
  9. package/src/components/ConnectionErrorView.tsx +272 -0
  10. package/src/components/ContextSwitcher.tsx +481 -0
  11. package/src/components/DebugOverlay.tsx +94 -0
  12. package/src/components/UserMenu.tsx +87 -0
  13. package/src/components/audit/AuditSettingsDialog.tsx +162 -0
  14. package/src/components/audit/AuditView.tsx +123 -0
  15. package/src/components/cost/CostTrendChart.tsx +388 -0
  16. package/src/components/cost/CostView.tsx +545 -0
  17. package/src/components/dock/BottomDock.tsx +96 -0
  18. package/src/components/dock/DockContext.tsx +11 -0
  19. package/src/components/dock/LocalTerminalTab.tsx +22 -0
  20. package/src/components/dock/LogsTab.tsx +26 -0
  21. package/src/components/dock/NodeTerminalTab.tsx +50 -0
  22. package/src/components/dock/TerminalTab.tsx +42 -0
  23. package/src/components/dock/TrafficFlowListTab.tsx +18 -0
  24. package/src/components/dock/WorkloadLogsTab.tsx +23 -0
  25. package/src/components/dock/index.ts +2 -0
  26. package/src/components/gitops/GitOpsActions.tsx +1 -0
  27. package/src/components/gitops/GitOpsStatusBadge.tsx +1 -0
  28. package/src/components/gitops/ManagedResourcesList.tsx +1 -0
  29. package/src/components/gitops/SyncCountdown.tsx +1 -0
  30. package/src/components/gitops/index.ts +4 -0
  31. package/src/components/helm/ChartBrowser.tsx +580 -0
  32. package/src/components/helm/HelmReleaseDrawer.tsx +774 -0
  33. package/src/components/helm/HelmView.tsx +475 -0
  34. package/src/components/helm/InstallWizard.tsx +1060 -0
  35. package/src/components/helm/ManifestDiffViewer.tsx +91 -0
  36. package/src/components/helm/ManifestViewer.tsx +61 -0
  37. package/src/components/helm/OwnedResources.tsx +465 -0
  38. package/src/components/helm/RevisionHistory.tsx +167 -0
  39. package/src/components/helm/ValuesDiffPreview.tsx +190 -0
  40. package/src/components/helm/ValuesViewer.tsx +365 -0
  41. package/src/components/helm/helm-utils.ts +37 -0
  42. package/src/components/home/ActivitySummary.tsx +262 -0
  43. package/src/components/home/CertificateHealthCard.tsx +105 -0
  44. package/src/components/home/ClusterHealthCard.tsx +483 -0
  45. package/src/components/home/CostCard.tsx +112 -0
  46. package/src/components/home/HealthRing.tsx +1 -0
  47. package/src/components/home/HelmSummary.tsx +129 -0
  48. package/src/components/home/HomeView.tsx +224 -0
  49. package/src/components/home/MCPSetupDialog.tsx +417 -0
  50. package/src/components/home/NetworkPolicyCoverageCard.tsx +109 -0
  51. package/src/components/home/TopologyPreview.tsx +219 -0
  52. package/src/components/home/TrafficSummary.tsx +154 -0
  53. package/src/components/logs/JsonLogLine.tsx +1 -0
  54. package/src/components/logs/LogCore.tsx +2 -0
  55. package/src/components/logs/LogsViewer.tsx +44 -0
  56. package/src/components/logs/WorkloadLogsViewer.tsx +40 -0
  57. package/src/components/logs/useLogBuffer.ts +2 -0
  58. package/src/components/logs/useLogSearch.ts +1 -0
  59. package/src/components/portforward/PortForwardButton.tsx +375 -0
  60. package/src/components/portforward/PortForwardManager.tsx +871 -0
  61. package/src/components/resource/PrometheusCharts.tsx +687 -0
  62. package/src/components/resource-drawer/ResourceDrawer.tsx +214 -0
  63. package/src/components/resources/ImageFilesystemModal.tsx +745 -0
  64. package/src/components/resources/PodFilesystemModal.tsx +407 -0
  65. package/src/components/resources/ResourceDetailDrawer.tsx +43 -0
  66. package/src/components/resources/ResourcesView.tsx +190 -0
  67. package/src/components/resources/drawer-components.tsx +1 -0
  68. package/src/components/resources/file-browser-utils.ts +35 -0
  69. package/src/components/resources/renderers/AlertRenderer.tsx +1 -0
  70. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +17 -0
  71. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +1 -0
  72. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -0
  73. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +1 -0
  74. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +1 -0
  75. package/src/components/resources/renderers/CertificateRenderer.tsx +1 -0
  76. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +1 -0
  77. package/src/components/resources/renderers/ChallengeRenderer.tsx +1 -0
  78. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +1 -0
  79. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +1 -0
  80. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +1 -0
  81. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +1 -0
  82. package/src/components/resources/renderers/ConfigMapRenderer.tsx +1 -0
  83. package/src/components/resources/renderers/CronJobRenderer.tsx +1 -0
  84. package/src/components/resources/renderers/EventRenderer.tsx +1 -0
  85. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +1 -0
  86. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +1 -0
  87. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -0
  88. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +1 -0
  89. package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -0
  90. package/src/components/resources/renderers/GatewayRenderer.tsx +1 -0
  91. package/src/components/resources/renderers/GenericRenderer.tsx +1 -0
  92. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +1 -0
  93. package/src/components/resources/renderers/HPARenderer.tsx +1 -0
  94. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +1 -0
  95. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -0
  96. package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -0
  97. package/src/components/resources/renderers/IngressRenderer.tsx +1 -0
  98. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +1 -0
  99. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -0
  100. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +1 -0
  101. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +1 -0
  102. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +1 -0
  103. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +1 -0
  104. package/src/components/resources/renderers/JobRenderer.tsx +1 -0
  105. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +1 -0
  106. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +1 -0
  107. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +1 -0
  108. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +1 -0
  109. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +1 -0
  110. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +1 -0
  111. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +1 -0
  112. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +1 -0
  113. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +1 -0
  114. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +1 -0
  115. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +1 -0
  116. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -0
  117. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +1 -0
  118. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +1 -0
  119. package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -0
  120. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +1 -0
  121. package/src/components/resources/renderers/LeaseRenderer.tsx +1 -0
  122. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +1 -0
  123. package/src/components/resources/renderers/NodeRenderer.tsx +44 -0
  124. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +1 -0
  125. package/src/components/resources/renderers/OrderRenderer.tsx +1 -0
  126. package/src/components/resources/renderers/PVCRenderer.tsx +1 -0
  127. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -0
  128. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -0
  129. package/src/components/resources/renderers/PodMonitorRenderer.tsx +1 -0
  130. package/src/components/resources/renderers/PodRenderer.tsx +94 -0
  131. package/src/components/resources/renderers/PriorityClassRenderer.tsx +1 -0
  132. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +1 -0
  133. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +1 -0
  134. package/src/components/resources/renderers/RoleBindingRenderer.tsx +1 -0
  135. package/src/components/resources/renderers/RoleRenderer.tsx +1 -0
  136. package/src/components/resources/renderers/RolloutRenderer.tsx +1 -0
  137. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +1 -0
  138. package/src/components/resources/renderers/SbomReportRenderer.tsx +1 -0
  139. package/src/components/resources/renderers/SealedSecretRenderer.tsx +1 -0
  140. package/src/components/resources/renderers/SecretRenderer.tsx +1 -0
  141. package/src/components/resources/renderers/SecretStoreRenderer.tsx +1 -0
  142. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -0
  143. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +1 -0
  144. package/src/components/resources/renderers/ServiceRenderer.tsx +26 -0
  145. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -0
  146. package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -0
  147. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -0
  148. package/src/components/resources/renderers/VPARenderer.tsx +1 -0
  149. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +1 -0
  150. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +1 -0
  151. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +1 -0
  152. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +1 -0
  153. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -0
  154. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +1 -0
  155. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +1 -0
  156. package/src/components/resources/renderers/WorkflowRenderer.tsx +1 -0
  157. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +1 -0
  158. package/src/components/resources/renderers/WorkloadRenderer.tsx +52 -0
  159. package/src/components/resources/renderers/argo-cells.tsx +1 -0
  160. package/src/components/resources/renderers/certmanager-cells.tsx +1 -0
  161. package/src/components/resources/renderers/cnpg-cells.tsx +1 -0
  162. package/src/components/resources/renderers/eso-cells.tsx +1 -0
  163. package/src/components/resources/renderers/flux-cells.tsx +1 -0
  164. package/src/components/resources/renderers/index.ts +91 -0
  165. package/src/components/resources/renderers/istio-cells.tsx +1 -0
  166. package/src/components/resources/renderers/karpenter-cells.tsx +1 -0
  167. package/src/components/resources/renderers/keda-cells.tsx +1 -0
  168. package/src/components/resources/renderers/knative-cells.tsx +1 -0
  169. package/src/components/resources/renderers/kyverno-cells.tsx +1 -0
  170. package/src/components/resources/renderers/prometheus-cells.tsx +1 -0
  171. package/src/components/resources/renderers/traefik-cells.tsx +1 -0
  172. package/src/components/resources/renderers/trivy-cells.tsx +1 -0
  173. package/src/components/resources/renderers/trivy-shared.tsx +1 -0
  174. package/src/components/resources/renderers/velero-cells.tsx +1 -0
  175. package/src/components/resources/resource-utils-argo.ts +2 -0
  176. package/src/components/resources/resource-utils-certmanager.ts +2 -0
  177. package/src/components/resources/resource-utils-cnpg.ts +2 -0
  178. package/src/components/resources/resource-utils-eso.ts +2 -0
  179. package/src/components/resources/resource-utils-flux.ts +2 -0
  180. package/src/components/resources/resource-utils-istio.ts +2 -0
  181. package/src/components/resources/resource-utils-karpenter.ts +2 -0
  182. package/src/components/resources/resource-utils-keda.ts +2 -0
  183. package/src/components/resources/resource-utils-knative.ts +2 -0
  184. package/src/components/resources/resource-utils-kyverno.ts +2 -0
  185. package/src/components/resources/resource-utils-prometheus.ts +2 -0
  186. package/src/components/resources/resource-utils-traefik.ts +1 -0
  187. package/src/components/resources/resource-utils-trivy.ts +2 -0
  188. package/src/components/resources/resource-utils-velero.ts +2 -0
  189. package/src/components/resources/resource-utils.ts +5 -0
  190. package/src/components/settings/SettingsDialog.tsx +537 -0
  191. package/src/components/shared/CreateResourceDialog.tsx +17 -0
  192. package/src/components/shared/EditableYamlView.tsx +24 -0
  193. package/src/components/shared/LargeClusterNamespacePicker.tsx +70 -0
  194. package/src/components/shared/ResourceRendererDispatch.tsx +31 -0
  195. package/src/components/timeline/DiffViewer.tsx +1 -0
  196. package/src/components/timeline/TimelineList.tsx +69 -0
  197. package/src/components/timeline/TimelineSwimlanes.tsx +1308 -0
  198. package/src/components/timeline/TimelineView.tsx +157 -0
  199. package/src/components/timeline/shared.tsx +1 -0
  200. package/src/components/traffic/TrafficFilterSidebar.tsx +571 -0
  201. package/src/components/traffic/TrafficFlowList.tsx +415 -0
  202. package/src/components/traffic/TrafficFlowListContext.tsx +68 -0
  203. package/src/components/traffic/TrafficGraph.tsx +1546 -0
  204. package/src/components/traffic/TrafficView.tsx +1213 -0
  205. package/src/components/traffic/TrafficWizard.tsx +386 -0
  206. package/src/components/traffic/index.ts +3 -0
  207. package/src/components/ui/CodeViewer.tsx +8 -0
  208. package/src/components/ui/CommandPalette.tsx +460 -0
  209. package/src/components/ui/ConfirmDialog.tsx +1 -0
  210. package/src/components/ui/DiagnosticsOverlay.tsx +619 -0
  211. package/src/components/ui/ErrorBoundary.tsx +46 -0
  212. package/src/components/ui/ForceDeleteConfirmDialog.tsx +1 -0
  213. package/src/components/ui/Markdown.tsx +108 -0
  214. package/src/components/ui/MetricsChart.tsx +1 -0
  215. package/src/components/ui/NamespaceSelector.tsx +436 -0
  216. package/src/components/ui/ResourceBar.tsx +1 -0
  217. package/src/components/ui/ShortcutHelpOverlay.tsx +301 -0
  218. package/src/components/ui/Toast.tsx +1 -0
  219. package/src/components/ui/Tooltip.tsx +1 -0
  220. package/src/components/ui/UpdateNotification.tsx +299 -0
  221. package/src/components/ui/YamlEditor.tsx +1 -0
  222. package/src/components/workload/WorkloadView.tsx +532 -0
  223. package/src/context/ConnectionContext.tsx +173 -0
  224. package/src/context/ContextSwitchContext.tsx +56 -0
  225. package/src/context/NavCustomization.tsx +62 -0
  226. package/src/context/ThemeContext.tsx +97 -0
  227. package/src/contexts/CapabilitiesContext.tsx +130 -0
  228. package/src/hooks/useAnimatedUnmount.ts +1 -0
  229. package/src/hooks/useDesktopDownload.ts +41 -0
  230. package/src/hooks/useEventSource.ts +262 -0
  231. package/src/hooks/useFavorites.ts +69 -0
  232. package/src/hooks/useKeyboardShortcuts.tsx +7 -0
  233. package/src/hooks/useRefreshAnimation.ts +1 -0
  234. package/src/index.css +243 -0
  235. package/src/index.ts +17 -0
  236. package/src/main.tsx +158 -0
  237. package/src/types/gitops.ts +2 -0
  238. package/src/types.ts +3 -0
  239. package/src/utils/animation.ts +2 -0
  240. package/src/utils/badge-colors.ts +2 -0
  241. package/src/utils/context-name.ts +2 -0
  242. package/src/utils/desktop-download.ts +66 -0
  243. package/src/utils/desktop-open-folder.ts +21 -0
  244. package/src/utils/format.ts +2 -0
  245. package/src/utils/log-format.ts +12 -0
  246. package/src/utils/navigation.ts +23 -0
  247. package/src/utils/resource-hierarchy.ts +2 -0
  248. package/src/utils/resource-icons.ts +2 -0
  249. package/src/utils/skeleton-yaml.ts +2 -0
  250. package/src/utils/traffic-colors.ts +54 -0
  251. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,219 @@
1
+ import { useMemo } from 'react'
2
+ import type { Topology } from '../../types'
3
+ import type { DashboardTopologySummary } from '../../api/client'
4
+ import { Network, ArrowRight } from 'lucide-react'
5
+ import { clsx } from 'clsx'
6
+
7
+ interface TopologyPreviewProps {
8
+ topology: Topology | null
9
+ summary: DashboardTopologySummary
10
+ onNavigate: () => void
11
+ }
12
+
13
+ /**
14
+ * Static schematic SVG that hints at the topology graph layout.
15
+ * Two groups with interconnected nodes using real topology color palette.
16
+ */
17
+ function TopologySchematic() {
18
+ // Colors matching the real topology graph
19
+ const ingress = '#a78bfa' // violet-400
20
+ const service = '#60a5fa' // blue-400
21
+ const deploy = '#34d399' // emerald-400
22
+ const pod = '#84cc16' // lime-500
23
+ const config = '#fbbf24' // amber-400
24
+ const groupBorder = 'rgba(59, 130, 246, 0.25)'
25
+ const groupBg = 'rgba(59, 130, 246, 0.04)'
26
+
27
+ // Edge color — use a concrete color so markers render reliably
28
+ const ec = '#64748b' // slate-500
29
+
30
+ return (
31
+ <svg viewBox="0 0 160 120" className="w-full h-full" aria-hidden="true">
32
+ <defs>
33
+ <marker id="arr" viewBox="0 0 8 6" refX="7" refY="3"
34
+ markerWidth="6" markerHeight="5" orient="auto">
35
+ <path d="M 0 0 L 8 3 L 0 6 z" fill={ec} opacity="0.6" />
36
+ </marker>
37
+ <marker id="arr-f" viewBox="0 0 8 6" refX="7" refY="3"
38
+ markerWidth="6" markerHeight="5" orient="auto">
39
+ <path d="M 0 0 L 8 3 L 0 6 z" fill={ec} opacity="0.35" />
40
+ </marker>
41
+ </defs>
42
+
43
+ {/* Group 1 — top: Ingress → Service → Deploys → Pods */}
44
+ <rect x="4" y="4" width="100" height="52" rx="6"
45
+ fill={groupBg} stroke={groupBorder} strokeWidth="1" strokeDasharray="3 2" />
46
+
47
+ {/* Nodes */}
48
+ <rect x="10" y="24" width="16" height="10" rx="3" fill={ingress} opacity="0.85" />
49
+ <rect x="38" y="24" width="16" height="10" rx="3" fill={service} opacity="0.85" />
50
+ <rect x="66" y="14" width="16" height="10" rx="3" fill={deploy} opacity="0.85" />
51
+ <rect x="66" y="34" width="16" height="10" rx="3" fill={deploy} opacity="0.85" />
52
+ <circle cx="96" cy="19" r="4.5" fill={pod} opacity="0.65" />
53
+ <circle cx="96" cy="39" r="4.5" fill={pod} opacity="0.65" />
54
+
55
+ {/* Edges — group 1 */}
56
+ <line x1="26" y1="29" x2="37" y2="29" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
57
+ <line x1="54" y1="29" x2="65" y2="20" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
58
+ <line x1="54" y1="29" x2="65" y2="38" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
59
+ <line x1="82" y1="19" x2="90" y2="19" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
60
+ <line x1="82" y1="39" x2="90" y2="39" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
61
+
62
+ {/* Group 2 — bottom: Service → Deploys → Pods + ConfigMap */}
63
+ <rect x="30" y="64" width="126" height="52" rx="6"
64
+ fill={groupBg} stroke={groupBorder} strokeWidth="1" strokeDasharray="3 2" />
65
+
66
+ {/* Nodes */}
67
+ <rect x="36" y="82" width="16" height="10" rx="3" fill={service} opacity="0.85" />
68
+ <rect x="66" y="72" width="16" height="10" rx="3" fill={deploy} opacity="0.85" />
69
+ <rect x="66" y="92" width="16" height="10" rx="3" fill={deploy} opacity="0.85" />
70
+ <circle cx="96" cy="73" r="4.5" fill={pod} opacity="0.65" />
71
+ <circle cx="96" cy="87" r="4.5" fill={pod} opacity="0.65" />
72
+ <circle cx="96" cy="101" r="4.5" fill={pod} opacity="0.65" />
73
+
74
+ {/* Config node */}
75
+ <rect x="120" y="82" width="16" height="10" rx="3" fill={config} opacity="0.55" />
76
+
77
+ {/* Edges — group 2 */}
78
+ <line x1="52" y1="87" x2="65" y2="78" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
79
+ <line x1="52" y1="87" x2="65" y2="96" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
80
+ <line x1="82" y1="77" x2="90" y2="74" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
81
+ <line x1="82" y1="77" x2="90" y2="87" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
82
+ <line x1="82" y1="97" x2="90" y2="101" stroke={ec} strokeWidth="1" opacity="0.55" markerEnd="url(#arr)" />
83
+
84
+ {/* Config dashed edge */}
85
+ <line x1="82" y1="77" x2="119" y2="87" stroke={ec} strokeWidth="0.7" opacity="0.3" strokeDasharray="2 2" markerEnd="url(#arr-f)" />
86
+
87
+ {/* Cross-group curved edge */}
88
+ <path d="M 18 34 Q 18 64, 36 82" fill="none" stroke={ec} strokeWidth="0.8" opacity="0.3" markerEnd="url(#arr-f)" />
89
+ </svg>
90
+ )
91
+ }
92
+
93
+ // Derive stats from real topology data
94
+ function useTopologyStats(topology: Topology | null) {
95
+ return useMemo(() => {
96
+ if (!topology || topology.nodes.length === 0) return null
97
+
98
+ const kindCounts: Record<string, number> = {}
99
+ const healthCounts = { healthy: 0, degraded: 0, unhealthy: 0, unknown: 0 }
100
+
101
+ for (const node of topology.nodes) {
102
+ kindCounts[node.kind] = (kindCounts[node.kind] || 0) + 1
103
+ if (node.status in healthCounts) {
104
+ healthCounts[node.status as keyof typeof healthCounts]++
105
+ }
106
+ }
107
+
108
+ // Top kinds sorted by display priority
109
+ const kindPriority: Record<string, number> = {
110
+ Deployment: 1, Rollout: 1, StatefulSet: 2, DaemonSet: 2,
111
+ Service: 3, Ingress: 4, Gateway: 4,
112
+ HTTPRoute: 4, GRPCRoute: 4, TCPRoute: 4, TLSRoute: 4,
113
+ Pod: 5, PodGroup: 5,
114
+ Job: 6, CronJob: 6, ConfigMap: 7, Secret: 7,
115
+ }
116
+ const topKinds = Object.entries(kindCounts)
117
+ .filter(([kind]) => kind !== 'Internet')
118
+ .sort(([a], [b]) => (kindPriority[a] || 99) - (kindPriority[b] || 99))
119
+ .slice(0, 8)
120
+
121
+ return { topKinds, healthCounts }
122
+ }, [topology])
123
+ }
124
+
125
+ const kindDotColors: Record<string, string> = {
126
+ Deployment: 'bg-emerald-400', Rollout: 'bg-emerald-400',
127
+ StatefulSet: 'bg-cyan-400', DaemonSet: 'bg-teal-400',
128
+ Service: 'bg-blue-400', Ingress: 'bg-violet-400', Gateway: 'bg-violet-400',
129
+ HTTPRoute: 'bg-purple-400', GRPCRoute: 'bg-purple-400', TCPRoute: 'bg-purple-400', TLSRoute: 'bg-purple-400',
130
+ Pod: 'bg-lime-500', PodGroup: 'bg-lime-500',
131
+ Job: 'bg-purple-400', CronJob: 'bg-purple-400',
132
+ ConfigMap: 'bg-amber-400', Secret: 'bg-red-400',
133
+ ReplicaSet: 'bg-green-400', HPA: 'bg-pink-500', PVC: 'bg-cyan-400',
134
+ }
135
+
136
+ export function TopologyPreview({ topology, summary, onNavigate }: TopologyPreviewProps) {
137
+ const stats = useTopologyStats(topology)
138
+
139
+ return (
140
+ <button
141
+ onClick={onNavigate}
142
+ className="group h-[260px] rounded-xl bg-theme-surface shadow-theme-sm hover:-translate-y-1 hover:shadow-theme-md transition-all duration-200 text-left overflow-hidden"
143
+ >
144
+ <div className="flex flex-col h-full w-full">
145
+ <div className="flex items-center justify-between px-5 py-3 border-b border-theme-border/50">
146
+ <div className="flex items-center gap-2">
147
+ <Network className="w-4 h-4 text-theme-text-tertiary" />
148
+ <span className="text-xs font-semibold uppercase tracking-wider text-theme-text-secondary">Topology</span>
149
+ </div>
150
+ <span className="text-[11px] text-theme-text-tertiary">
151
+ {summary.nodeCount} resources &middot; {summary.edgeCount} conn
152
+ </span>
153
+ </div>
154
+
155
+ {/* Stats (left) + Schematic (right) */}
156
+ <div className="flex-1 flex items-stretch min-h-0 px-3 py-1.5 gap-2">
157
+ {/* Left: compact stats */}
158
+ <div className="flex flex-col justify-center gap-0.5 min-w-0 w-[105px] shrink-0">
159
+ {stats ? (
160
+ <>
161
+ {stats.topKinds.map(([kind, count]) => (
162
+ <div key={kind} className="flex items-center gap-1.5 text-[10px] leading-tight">
163
+ <span className={clsx(
164
+ 'w-1.5 h-1.5 rounded-full shrink-0',
165
+ kindDotColors[kind] || 'bg-theme-text-tertiary',
166
+ )} />
167
+ <span className="text-theme-text-primary font-medium w-5 text-right tabular-nums">{count}</span>
168
+ <span className="text-theme-text-tertiary truncate">{kind}</span>
169
+ </div>
170
+ ))}
171
+
172
+ {(stats.healthCounts.degraded > 0 || stats.healthCounts.unhealthy > 0) && (
173
+ <div className="flex items-center gap-1.5 text-[10px] text-theme-text-tertiary mt-0.5 pt-1 border-t border-theme-border/50">
174
+ {stats.healthCounts.unhealthy > 0 && (
175
+ <span className="flex items-center gap-0.5">
176
+ <span className="w-1.5 h-1.5 rounded-full bg-red-500" />
177
+ {stats.healthCounts.unhealthy}
178
+ </span>
179
+ )}
180
+ {stats.healthCounts.degraded > 0 && (
181
+ <span className="flex items-center gap-0.5">
182
+ <span className="w-1.5 h-1.5 rounded-full bg-yellow-500" />
183
+ {stats.healthCounts.degraded}
184
+ </span>
185
+ )}
186
+ </div>
187
+ )}
188
+ </>
189
+ ) : (
190
+ // Show summary-based placeholder while full topology loads via SSE
191
+ <div className="flex flex-col gap-0.5">
192
+ <div className="flex items-center gap-1.5 text-[10px] leading-tight">
193
+ <span className="w-1.5 h-1.5 rounded-full bg-blue-400 shrink-0" />
194
+ <span className="text-theme-text-primary font-medium w-5 text-right tabular-nums">{summary.nodeCount}</span>
195
+ <span className="text-theme-text-tertiary">resources</span>
196
+ </div>
197
+ <div className="flex items-center gap-1.5 text-[10px] leading-tight">
198
+ <span className="w-1.5 h-1.5 rounded-full bg-theme-text-tertiary shrink-0" />
199
+ <span className="text-theme-text-primary font-medium w-5 text-right tabular-nums">{summary.edgeCount}</span>
200
+ <span className="text-theme-text-tertiary">connections</span>
201
+ </div>
202
+ </div>
203
+ )}
204
+ </div>
205
+
206
+ {/* Right: schematic illustration */}
207
+ <div className="flex-1 flex items-center min-w-0">
208
+ <TopologySchematic />
209
+ </div>
210
+ </div>
211
+
212
+ <div className="px-4 py-1.5 border-t border-theme-border/50 flex items-center justify-end gap-1.5 text-[10px] font-semibold uppercase tracking-wider text-theme-text-secondary group-hover:text-theme-text-primary transition-colors">
213
+ Open Topology
214
+ <ArrowRight className="w-3.5 h-3.5 transition-transform group-hover:translate-x-0.5" />
215
+ </div>
216
+ </div>
217
+ </button>
218
+ )
219
+ }
@@ -0,0 +1,154 @@
1
+ import type { DashboardTrafficSummary } from '../../api/client'
2
+ import { Activity, ArrowRight, ArrowRightLeft } from 'lucide-react'
3
+
4
+ interface TrafficSummaryProps {
5
+ data: DashboardTrafficSummary | null
6
+ onNavigate: () => void
7
+ }
8
+
9
+ /**
10
+ * Static schematic SVG mimicking the real traffic view layout:
11
+ * vertical spine on the left, Internet node, chunky arrows branching
12
+ * right to colored service nodes, some with further connections.
13
+ */
14
+ function TrafficSchematic() {
15
+ // Service node colors matching the real traffic view
16
+ const green = '#22c55e' // skyhook-connector style
17
+ const blue = '#3b82f6' // dev services
18
+ const brown = '#a3734c' // staging services
19
+ const teal = '#14b8a6' // keda style
20
+ const red = '#ef4444' // unhealthy / alert
21
+ const purple = '#a855f7' // envoy / system
22
+ const inet = '#93c5fd' // light blue — Internet node bg
23
+ const inetBorder = '#60a5fa'
24
+
25
+ // Edge color
26
+ const ec = '#6b7280' // gray-500
27
+
28
+ return (
29
+ <svg viewBox="0 0 160 120" className="w-full h-auto max-h-full" aria-hidden="true">
30
+ <defs>
31
+ <marker id="trf-a" viewBox="0 0 10 8" refX="9" refY="4"
32
+ markerWidth="7" markerHeight="6" orient="auto">
33
+ <path d="M 0 0 L 10 4 L 0 8 z" fill={ec} opacity="0.55" />
34
+ </marker>
35
+ <marker id="trf-a2" viewBox="0 0 10 8" refX="9" refY="4"
36
+ markerWidth="7" markerHeight="6" orient="auto">
37
+ <path d="M 0 0 L 10 4 L 0 8 z" fill={ec} opacity="0.35" />
38
+ </marker>
39
+ </defs>
40
+
41
+ {/* Vertical spine */}
42
+ <line x1="38" y1="6" x2="38" y2="114" stroke={ec} strokeWidth="2.5" opacity="0.3" strokeLinecap="round" />
43
+
44
+ {/* Internet node — rounded rect with light fill */}
45
+ <rect x="3" y="43" width="24" height="16" rx="5" fill={inet} opacity="0.35" stroke={inetBorder} strokeWidth="0.8" />
46
+ <circle cx="12" cy="51" r="2.5" fill={inetBorder} opacity="0.6" />
47
+
48
+ {/* Arrow: Internet → spine */}
49
+ <line x1="27" y1="51" x2="36" y2="51" stroke={ec} strokeWidth="2.5" opacity="0.45" markerEnd="url(#trf-a)" />
50
+
51
+ {/* === Branch 1: green service (top) === */}
52
+ <line x1="39" y1="14" x2="56" y2="14" stroke={ec} strokeWidth="2" opacity="0.4" markerEnd="url(#trf-a)" />
53
+ <rect x="58" y="8" width="28" height="12" rx="3" fill={green} opacity="0.85" />
54
+ {/* status dot */}
55
+ <circle cx="62" cy="14" r="1.5" fill="#fbbf24" opacity="0.9" />
56
+
57
+ {/* === Branch 2: blue service → right node === */}
58
+ <line x1="39" y1="33" x2="56" y2="33" stroke={ec} strokeWidth="1.8" opacity="0.4" markerEnd="url(#trf-a)" />
59
+ <rect x="58" y="27" width="28" height="12" rx="3" fill={blue} opacity="0.85" />
60
+ <circle cx="62" cy="33" r="1.5" fill={green} opacity="0.9" />
61
+ {/* → right node (external) */}
62
+ <line x1="86" y1="33" x2="108" y2="24" stroke={ec} strokeWidth="1.2" opacity="0.35" markerEnd="url(#trf-a2)" />
63
+ <rect x="110" y="18" width="28" height="12" rx="3" fill={red} opacity="0.7" />
64
+
65
+ {/* === Branch 3: brown service === */}
66
+ <line x1="39" y1="51" x2="56" y2="51" stroke={ec} strokeWidth="2.2" opacity="0.45" markerEnd="url(#trf-a)" />
67
+ <rect x="58" y="45" width="28" height="12" rx="3" fill={brown} opacity="0.85" />
68
+ <circle cx="62" cy="51" r="1.5" fill={green} opacity="0.9" />
69
+
70
+ {/* === Branch 4: teal service → right node === */}
71
+ <line x1="39" y1="69" x2="56" y2="69" stroke={ec} strokeWidth="1.5" opacity="0.38" markerEnd="url(#trf-a)" />
72
+ <rect x="58" y="63" width="28" height="12" rx="3" fill={teal} opacity="0.85" />
73
+ <circle cx="62" cy="69" r="1.5" fill={green} opacity="0.9" />
74
+ {/* → right node */}
75
+ <line x1="86" y1="69" x2="108" y2="76" stroke={ec} strokeWidth="1.2" opacity="0.35" markerEnd="url(#trf-a2)" />
76
+ <rect x="110" y="70" width="28" height="12" rx="3" fill={blue} opacity="0.7" />
77
+
78
+ {/* === Branch 5: purple service (bottom) === */}
79
+ <line x1="39" y1="87" x2="56" y2="87" stroke={ec} strokeWidth="1.3" opacity="0.35" markerEnd="url(#trf-a)" />
80
+ <rect x="58" y="81" width="28" height="12" rx="3" fill={purple} opacity="0.85" />
81
+ <circle cx="62" cy="87" r="1.5" fill={green} opacity="0.9" />
82
+
83
+ {/* === Branch 6: small red service === */}
84
+ <line x1="39" y1="103" x2="56" y2="103" stroke={ec} strokeWidth="1" opacity="0.3" markerEnd="url(#trf-a)" />
85
+ <rect x="58" y="97" width="28" height="12" rx="3" fill={red} opacity="0.65" />
86
+ <circle cx="62" cy="103" r="1.5" fill={red} opacity="0.9" />
87
+ </svg>
88
+ )
89
+ }
90
+
91
+ export function TrafficSummary({ data, onNavigate }: TrafficSummaryProps) {
92
+ const hasFlows = data && data.flowCount > 0
93
+
94
+ return (
95
+ <button
96
+ onClick={onNavigate}
97
+ className="group h-[260px] rounded-xl bg-theme-surface shadow-theme-sm hover:-translate-y-1 hover:shadow-theme-md transition-all duration-200 text-left overflow-hidden"
98
+ >
99
+ <div className="flex flex-col h-full w-full">
100
+ <div className="flex items-center justify-between px-5 py-3 border-b border-theme-border/50">
101
+ <div className="flex items-center gap-2">
102
+ <Activity className="w-4 h-4 text-theme-text-tertiary" />
103
+ <span className="text-xs font-semibold uppercase tracking-wider text-theme-text-secondary">Traffic</span>
104
+ </div>
105
+ {hasFlows && (
106
+ <span className="text-[11px] text-theme-text-tertiary">
107
+ {data.source} &middot; {data.flowCount} flows
108
+ </span>
109
+ )}
110
+ </div>
111
+
112
+ <div className="flex-1 min-h-0 overflow-hidden">
113
+ {hasFlows ? (
114
+ /* Real data: show top flows */
115
+ <div className="px-4 py-2 space-y-1.5">
116
+ {data.topFlows.map((flow, i) => (
117
+ <div key={i} className="flex items-center gap-2 text-xs">
118
+ <span className="text-theme-text-primary truncate">{flow.src}</span>
119
+ <ArrowRightLeft className="w-3 h-3 text-theme-text-tertiary shrink-0" />
120
+ <span className="text-theme-text-primary truncate">{flow.dst}</span>
121
+ <span className="text-xs text-theme-text-tertiary shrink-0 ml-auto">
122
+ {flow.connections} conn
123
+ </span>
124
+ </div>
125
+ ))}
126
+ </div>
127
+ ) : (
128
+ /* No data yet: schematic + description */
129
+ <div className="flex items-stretch gap-2 px-3 py-1.5 h-full">
130
+ <div className="flex flex-col justify-center gap-1.5 w-[105px] shrink-0">
131
+ <p className="text-[11px] leading-snug text-theme-text-secondary">
132
+ Visualize live service-to-service network flows and external dependencies
133
+ </p>
134
+ {data && (
135
+ <span className="text-[10px] text-theme-text-tertiary">
136
+ Source: {data.source}
137
+ </span>
138
+ )}
139
+ </div>
140
+ <div className="flex-1 min-w-0 overflow-hidden">
141
+ <TrafficSchematic />
142
+ </div>
143
+ </div>
144
+ )}
145
+ </div>
146
+
147
+ <div className="px-4 py-1.5 border-t border-theme-border/50 flex items-center justify-end gap-1.5 text-[10px] font-semibold uppercase tracking-wider text-theme-text-secondary group-hover:text-theme-text-primary transition-colors">
148
+ Open Traffic
149
+ <ArrowRight className="w-3.5 h-3.5 transition-transform group-hover:translate-x-0.5" />
150
+ </div>
151
+ </div>
152
+ </button>
153
+ )
154
+ }
@@ -0,0 +1 @@
1
+ export { StructuredLogLine as JsonLogLine } from '@skyhook-io/k8s-ui'
@@ -0,0 +1,2 @@
1
+ export { LogCore } from '@skyhook-io/k8s-ui'
2
+ export type { DownloadFormat } from '@skyhook-io/k8s-ui'
@@ -0,0 +1,44 @@
1
+ import { useCallback } from 'react'
2
+ import { fetchJSON, createLogStream } from '../../api/client'
3
+ import { LogsViewer as SharedLogsViewer } from '@skyhook-io/k8s-ui'
4
+ import type { LogsFetchParams } from '@skyhook-io/k8s-ui'
5
+ import { useDesktopDownload } from '../../hooks/useDesktopDownload'
6
+
7
+ interface LogsViewerProps {
8
+ namespace: string
9
+ podName: string
10
+ containers: string[]
11
+ initialContainer?: string
12
+ }
13
+
14
+ export function LogsViewer({ namespace, podName, containers, initialContainer }: LogsViewerProps) {
15
+ const desktopDownload = useDesktopDownload()
16
+
17
+ const fetchLogs = useCallback(async (params: LogsFetchParams) => {
18
+ const query = new URLSearchParams()
19
+ query.set('container', params.container)
20
+ if (params.tailLines) query.set('tailLines', String(params.tailLines))
21
+ if (params.sinceSeconds) query.set('sinceSeconds', String(params.sinceSeconds))
22
+ if (params.previous) query.set('previous', 'true')
23
+ const data = await fetchJSON<{ logs: { [container: string]: string } }>(
24
+ `/pods/${namespace}/${podName}/logs?${query}`
25
+ )
26
+ return data.logs
27
+ }, [namespace, podName])
28
+
29
+ const makeStream = useCallback((params: Omit<LogsFetchParams, 'previous'>) => {
30
+ return createLogStream(namespace, podName, params)
31
+ }, [namespace, podName])
32
+
33
+ return (
34
+ <SharedLogsViewer
35
+ namespace={namespace}
36
+ podName={podName}
37
+ containers={containers}
38
+ initialContainer={initialContainer}
39
+ fetchLogs={fetchLogs}
40
+ createStream={makeStream}
41
+ overrideDownload={desktopDownload}
42
+ />
43
+ )
44
+ }
@@ -0,0 +1,40 @@
1
+ import { useCallback } from 'react'
2
+ import { fetchJSON, createWorkloadLogStream } from '../../api/client'
3
+ import { WorkloadLogsViewer as SharedWorkloadLogsViewer } from '@skyhook-io/k8s-ui'
4
+ import type { WorkloadLogsFetchParams, WorkloadLogsResult } from '@skyhook-io/k8s-ui'
5
+ import { useDesktopDownload } from '../../hooks/useDesktopDownload'
6
+
7
+ interface WorkloadLogsViewerProps {
8
+ kind: string
9
+ namespace: string
10
+ name: string
11
+ }
12
+
13
+ export function WorkloadLogsViewer({ kind, namespace, name }: WorkloadLogsViewerProps) {
14
+ const desktopDownload = useDesktopDownload()
15
+
16
+ const fetchAll = useCallback(async (params: WorkloadLogsFetchParams): Promise<WorkloadLogsResult> => {
17
+ const query = new URLSearchParams()
18
+ if (params.container) query.set('container', params.container)
19
+ if (params.tailLines) query.set('tailLines', String(params.tailLines))
20
+ if (params.sinceSeconds) query.set('sinceSeconds', String(params.sinceSeconds))
21
+ const qs = query.toString()
22
+ const data = await fetchJSON<WorkloadLogsResult>(
23
+ `/workloads/${kind}/${namespace}/${name}/logs${qs ? `?${qs}` : ''}`
24
+ )
25
+ return data
26
+ }, [kind, namespace, name])
27
+
28
+ const makeStream = useCallback((params: WorkloadLogsFetchParams) => {
29
+ return createWorkloadLogStream(kind, namespace, name, params)
30
+ }, [kind, namespace, name])
31
+
32
+ return (
33
+ <SharedWorkloadLogsViewer
34
+ name={name}
35
+ fetchAll={fetchAll}
36
+ createStream={makeStream}
37
+ overrideDownload={desktopDownload}
38
+ />
39
+ )
40
+ }
@@ -0,0 +1,2 @@
1
+ export { useLogBuffer, detectLogLevel } from '@skyhook-io/k8s-ui'
2
+ export type { LogLevel, LogEntry } from '@skyhook-io/k8s-ui'
@@ -0,0 +1 @@
1
+ export { useLogSearch } from '@skyhook-io/k8s-ui'