@skyhook-io/radar-app 1.14.0 → 1.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/App.tsx +6 -4
- package/src/api/client.ts +3 -0
- package/src/api/diagnose.ts +31 -1
- package/src/components/CloudConnectFlow.tsx +173 -13
- package/src/components/CloudFunnelButton.tsx +4 -2
- package/src/components/ConnectionErrorView.tsx +9 -10
- package/src/components/DebugOverlay.tsx +10 -6
- package/src/components/cloudConnectHandoff.test.ts +109 -4
- package/src/components/cloudConnectHandoff.ts +154 -2
- package/src/components/curl/ServiceCurlButton.tsx +19 -26
- package/src/components/diagnose/AgentCase.tsx +18 -5
- package/src/components/diagnose/AnalysisStory.test.tsx +308 -0
- package/src/components/diagnose/AnalysisStory.tsx +564 -0
- package/src/components/diagnose/DiagnoseContext.test.ts +10 -0
- package/src/components/diagnose/DiagnoseContext.tsx +25 -8
- package/src/components/diagnose/DiagnoseSurface.tsx +20 -14
- package/src/components/diagnose/InvestigationEvidencePane.story.test.tsx +771 -0
- package/src/components/diagnose/InvestigationEvidencePane.test.tsx +8 -33
- package/src/components/diagnose/InvestigationEvidencePane.tsx +809 -168
- package/src/components/diagnose/InvestigationResourceEvidence.test.tsx +30 -0
- package/src/components/diagnose/InvestigationResourceEvidence.tsx +20 -0
- package/src/components/diagnose/InvestigationView.tsx +493 -529
- package/src/components/diagnose/investigationCase.test.tsx +267 -129
- package/src/components/diagnose/investigationCase.ts +122 -77
- package/src/components/diagnose/investigationEvidence/adapters/resource.test.ts +27 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.ts +28 -0
- package/src/components/diagnose/investigationEvidence/builder.ts +11 -2
- package/src/components/diagnose/investigationEvidence/identity.test.ts +25 -7
- package/src/components/diagnose/investigationEvidence/identity.ts +4 -4
- package/src/components/diagnose/investigationEvidence/observations.ts +24 -0
- package/src/components/diagnose/investigationEvidence/projection.test.ts +36 -3
- package/src/components/diagnose/investigationEvidence/types.ts +2 -0
- package/src/components/diagnose/investigationEvidencePresentation.test.ts +2 -0
- package/src/components/diagnose/investigationEvidencePresentation.ts +3 -0
- package/src/components/diagnose/investigationState.test.ts +316 -58
- package/src/components/diagnose/investigationState.ts +257 -44
- package/src/components/diagnose/investigationStory.test.ts +161 -0
- package/src/components/diagnose/investigationStory.ts +207 -0
- package/src/components/diagnose/parts.test.tsx +9 -6
- package/src/components/diagnose/parts.tsx +977 -193
- package/src/components/diagnose/storyParts.test.tsx +426 -0
- package/src/components/diagnose/toolCallLabel.test.ts +51 -0
- package/src/components/diagnose/toolCallLabel.ts +135 -0
- package/src/components/diagnose/useDisclosureReveal.ts +10 -11
- package/src/components/helm/HelmCompareRoute.tsx +18 -4
- package/src/components/helm/HelmReleaseDrawer.tsx +11 -26
- package/src/components/helm/InstallWizard.tsx +8 -3
- package/src/components/home/MCPSetupDialog.tsx +15 -9
- package/src/components/portforward/PortForwardManager.tsx +6 -13
- package/src/components/resource/PrometheusChartsGrid.tsx +3 -3
- package/src/components/resource/WorkloadMetricsHelpDialog.tsx +3 -3
- package/src/components/resource/WorkloadMetricsSection.tsx +15 -21
- package/src/components/resources/PodFilePreview.tsx +3 -3
- package/src/components/resources/renderers/ServiceRenderer.tsx +2 -1
- package/src/components/settings/SettingsDialog.tsx +4 -2
- package/src/components/ui/CommandPalette.tsx +10 -6
- package/src/components/ui/DiagnosticsOverlay.tsx +10 -6
- package/src/components/ui/Disclosure.tsx +47 -0
- package/src/components/ui/Markdown.tsx +23 -11
- package/src/components/ui/ShortcutHelpOverlay.tsx +3 -1
- package/src/components/ui/UpdateNotification.tsx +18 -2
- package/src/index.css +19 -6
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useState, type ReactNode } from 'react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Collapse, CollapseChevron, useDisclosure } from '@skyhook-io/k8s-ui/components/ui/Collapse'
|
|
4
|
+
|
|
5
|
+
// In-flow disclosure with the app-standard motion — the replacement for native
|
|
6
|
+
// <details>/<summary> on primary paths. Native details snaps open with no
|
|
7
|
+
// transition and its marker ignores the caret language; this pairs a button
|
|
8
|
+
// header (aria-expanded / aria-controls via useDisclosure) with CollapseChevron
|
|
9
|
+
// and an animated Collapse panel. Content stays mounted while closed, exactly
|
|
10
|
+
// like native details, so server-rendered markup and in-page search still see
|
|
11
|
+
// the collapsed copy.
|
|
12
|
+
export function Disclosure({
|
|
13
|
+
summary,
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
summaryClassName,
|
|
17
|
+
chevronClassName = 'h-3.5 w-3.5',
|
|
18
|
+
defaultOpen = false,
|
|
19
|
+
}: {
|
|
20
|
+
summary: ReactNode
|
|
21
|
+
children: ReactNode
|
|
22
|
+
/** Wrapper (what the old <details> carried). */
|
|
23
|
+
className?: string
|
|
24
|
+
/** Header button (what the old <summary> carried). */
|
|
25
|
+
summaryClassName?: string
|
|
26
|
+
chevronClassName?: string
|
|
27
|
+
defaultOpen?: boolean
|
|
28
|
+
}) {
|
|
29
|
+
const [open, setOpen] = useState(defaultOpen)
|
|
30
|
+
const disclosure = useDisclosure(open)
|
|
31
|
+
return (
|
|
32
|
+
<div className={className}>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
{...disclosure.buttonProps}
|
|
36
|
+
onClick={() => setOpen((value) => !value)}
|
|
37
|
+
className={clsx('flex w-full cursor-pointer items-center gap-1.5 text-left', summaryClassName)}
|
|
38
|
+
>
|
|
39
|
+
<CollapseChevron open={open} className={chevronClassName} />
|
|
40
|
+
{summary}
|
|
41
|
+
</button>
|
|
42
|
+
<Collapse open={open} id={disclosure.panelId}>
|
|
43
|
+
{children}
|
|
44
|
+
</Collapse>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -2,12 +2,20 @@ import ReactMarkdown from 'react-markdown'
|
|
|
2
2
|
import remarkGfm from 'remark-gfm'
|
|
3
3
|
import { clsx } from 'clsx'
|
|
4
4
|
|
|
5
|
+
import type { ReactNode } from 'react'
|
|
6
|
+
|
|
5
7
|
interface MarkdownProps {
|
|
6
8
|
children: string
|
|
7
9
|
className?: string
|
|
10
|
+
/**
|
|
11
|
+
* Intercepts links before the default anchor renders. Return null to fall
|
|
12
|
+
* through. Lets a caller turn a reserved href (an in-page evidence
|
|
13
|
+
* reference, say) into a control instead of an external link.
|
|
14
|
+
*/
|
|
15
|
+
linkRenderer?: (href: string | undefined, children: ReactNode) => ReactNode | null
|
|
8
16
|
}
|
|
9
17
|
|
|
10
|
-
export function Markdown({ children, className }: MarkdownProps) {
|
|
18
|
+
export function Markdown({ children, className, linkRenderer }: MarkdownProps) {
|
|
11
19
|
return (
|
|
12
20
|
<div className={clsx('markdown-content', className)}>
|
|
13
21
|
<ReactMarkdown
|
|
@@ -28,16 +36,20 @@ export function Markdown({ children, className }: MarkdownProps) {
|
|
|
28
36
|
p: ({ children }) => (
|
|
29
37
|
<p className="text-theme-text-secondary my-2 leading-relaxed">{children}</p>
|
|
30
38
|
),
|
|
31
|
-
a: ({ href, children }) =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
a: ({ href, children }) => {
|
|
40
|
+
const custom = linkRenderer?.(href, children)
|
|
41
|
+
if (custom !== null && custom !== undefined) return <>{custom}</>
|
|
42
|
+
return (
|
|
43
|
+
<a
|
|
44
|
+
href={href}
|
|
45
|
+
target="_blank"
|
|
46
|
+
rel="noopener noreferrer"
|
|
47
|
+
className="text-blue-400 hover:text-blue-300 underline underline-offset-2"
|
|
48
|
+
>
|
|
49
|
+
{children}
|
|
50
|
+
</a>
|
|
51
|
+
)
|
|
52
|
+
},
|
|
41
53
|
ul: ({ children }) => (
|
|
42
54
|
<ul className="list-disc list-outside pl-4 my-2 space-y-1 text-theme-text-secondary">{children}</ul>
|
|
43
55
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react'
|
|
2
2
|
import { X } from 'lucide-react'
|
|
3
3
|
import { clsx } from 'clsx'
|
|
4
|
-
import { TRANSITION_BACKDROP, TRANSITION_PANEL } from '../../utils/animation'
|
|
4
|
+
import { TRANSITION_BACKDROP, TRANSITION_PANEL, overlayTransitionStyle } from '../../utils/animation'
|
|
5
5
|
import { useActiveShortcuts, type ShortcutCategory } from '../../hooks/useKeyboardShortcuts'
|
|
6
6
|
|
|
7
7
|
interface ShortcutHelpOverlayProps {
|
|
@@ -232,6 +232,7 @@ export function ShortcutHelpOverlay({ onClose, currentView, isOpen = true }: Sho
|
|
|
232
232
|
TRANSITION_BACKDROP,
|
|
233
233
|
isOpen ? 'opacity-100' : 'opacity-0'
|
|
234
234
|
)}
|
|
235
|
+
style={overlayTransitionStyle(isOpen, 'dialog')}
|
|
235
236
|
onClick={onClose}
|
|
236
237
|
/>
|
|
237
238
|
|
|
@@ -243,6 +244,7 @@ export function ShortcutHelpOverlay({ onClose, currentView, isOpen = true }: Sho
|
|
|
243
244
|
TRANSITION_PANEL,
|
|
244
245
|
isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-[0.97]'
|
|
245
246
|
)}
|
|
247
|
+
style={overlayTransitionStyle(isOpen, 'dialog')}
|
|
246
248
|
>
|
|
247
249
|
{/* Header */}
|
|
248
250
|
<div className="flex items-center justify-between px-5 py-3.5 border-b border-theme-border">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
2
3
|
import { Download, X, Copy, Check, RotateCw, ArrowDownToLine, Loader2 } from 'lucide-react'
|
|
3
4
|
import { useQueryClient } from '@tanstack/react-query'
|
|
4
5
|
import {
|
|
@@ -10,6 +11,8 @@ import {
|
|
|
10
11
|
} from '../../api/client'
|
|
11
12
|
import type { DesktopUpdateState } from '../../api/client'
|
|
12
13
|
import { WithTooltip } from './Tooltip'
|
|
14
|
+
import { TRANSITION_MENU, overlayExitMs, overlayTransitionStyle } from '../../utils/animation'
|
|
15
|
+
import { useAnimatedUnmount } from '../../hooks/useAnimatedUnmount'
|
|
13
16
|
|
|
14
17
|
const DISMISSED_KEY = 'radar-update-dismissed'
|
|
15
18
|
|
|
@@ -103,7 +106,11 @@ export function UpdateNotification() {
|
|
|
103
106
|
|
|
104
107
|
// Shared in-cluster viewers get a persistent Home notice instead of a
|
|
105
108
|
// floating action prompt they may not be able to act on.
|
|
106
|
-
|
|
109
|
+
const show = !!versionInfo?.updateAvailable && !dismissed && deploymentMode !== undefined && deploymentMode !== 'in-cluster' && deploymentMode !== 'cloud'
|
|
110
|
+
// Presence outlives `show` by the menu exit so a dismiss fades the chip out
|
|
111
|
+
// instead of snapping it away; the enter runs the same transition in reverse.
|
|
112
|
+
const { shouldRender, isOpen } = useAnimatedUnmount(show, overlayExitMs('menu'))
|
|
113
|
+
if (!shouldRender || !versionInfo) {
|
|
107
114
|
return null
|
|
108
115
|
}
|
|
109
116
|
|
|
@@ -111,7 +118,16 @@ export function UpdateNotification() {
|
|
|
111
118
|
const effectiveState: DesktopUpdateState = updateStatus?.state ?? 'idle'
|
|
112
119
|
|
|
113
120
|
return (
|
|
114
|
-
<div
|
|
121
|
+
<div
|
|
122
|
+
inert={!show || undefined}
|
|
123
|
+
className={clsx(
|
|
124
|
+
'fixed bottom-4 right-4 z-50 max-w-sm bg-theme-surface border border-accent/50 rounded-lg shadow-xl p-4 origin-bottom-right',
|
|
125
|
+
TRANSITION_MENU,
|
|
126
|
+
isOpen ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 translate-y-1 scale-[0.97]',
|
|
127
|
+
!show && 'pointer-events-none',
|
|
128
|
+
)}
|
|
129
|
+
style={overlayTransitionStyle(isOpen, 'menu')}
|
|
130
|
+
>
|
|
115
131
|
<div className="flex items-start gap-3">
|
|
116
132
|
<div className="flex items-center justify-center w-8 h-8 bg-accent-muted rounded-full shrink-0">
|
|
117
133
|
<UpdateIcon state={effectiveState} />
|
package/src/index.css
CHANGED
|
@@ -344,10 +344,26 @@ body {
|
|
|
344
344
|
animation: status-enter 140ms ease-out both;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/* Reduced motion is an app-level policy, not a per-class list. Durations and
|
|
348
|
+
delays go to ~0 (a delay left in place would hold a fill-mode:both element
|
|
349
|
+
invisible), every loop runs once (an infinite animation at 0.01ms is still an
|
|
350
|
+
active animation — pulse, ping, Monaco's progress bit), and status spinners
|
|
351
|
+
keep turning because they convey state, not motion. Components that coordinate motion in
|
|
352
|
+
JS (presence hooks, drawer morph) check prefersReducedMotion() themselves and
|
|
353
|
+
dispose at logical close. */
|
|
347
354
|
@media (prefers-reduced-motion: reduce) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
animation:
|
|
355
|
+
*, *::before, *::after {
|
|
356
|
+
animation-duration: 0.01ms !important;
|
|
357
|
+
animation-delay: 0ms !important;
|
|
358
|
+
animation-iteration-count: 1 !important;
|
|
359
|
+
transition-duration: 0.01ms !important;
|
|
360
|
+
transition-delay: 0ms !important;
|
|
361
|
+
scroll-behavior: auto !important;
|
|
362
|
+
}
|
|
363
|
+
/* The one exception: a spinner that stops reads as "done", not "calm". */
|
|
364
|
+
.animate-spin {
|
|
365
|
+
animation-duration: 1s !important;
|
|
366
|
+
animation-iteration-count: infinite !important;
|
|
351
367
|
}
|
|
352
368
|
}
|
|
353
369
|
|
|
@@ -403,9 +419,6 @@ body {
|
|
|
403
419
|
-webkit-text-fill-color: currentColor;
|
|
404
420
|
color: var(--text-secondary);
|
|
405
421
|
}
|
|
406
|
-
.animate-result-in {
|
|
407
|
-
animation: none;
|
|
408
|
-
}
|
|
409
422
|
}
|
|
410
423
|
|
|
411
424
|
/* Gentle colorize when syntax-highlighted code swaps in over plain text — opacity
|