@skyhook-io/k8s-ui 1.8.1 → 1.8.3
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 +1 -1
- package/src/components/applications/AppTooltips.tsx +35 -8
- package/src/components/applications/ApplicationDetail.tsx +9 -2
- package/src/components/applications/ApplicationsList.tsx +17 -570
- package/src/components/applications/ApplicationsView.tsx +602 -0
- package/src/components/applications/applications-view.test.tsx +147 -0
- package/src/components/applications/index.ts +4 -0
- package/src/components/dock/LocalTerminalTab.tsx +22 -1
- package/src/components/dock/TerminalClipboardToolbar.tsx +35 -0
- package/src/components/dock/TerminalTab.tsx +22 -1
- package/src/components/dock/terminalClipboard.test.ts +221 -0
- package/src/components/dock/terminalClipboard.ts +122 -0
- package/src/components/dock/useMultilinePasteConfirm.tsx +75 -0
- package/src/components/gitops/GitOpsTableView.tsx +22 -2
- package/src/components/resources/ResourcesSidebar.tsx +16 -13
- package/src/components/resources/ResourcesView.tsx +50 -4
- package/src/components/resources/index.ts +1 -1
- package/src/hooks/useKeyboardShortcuts.tsx +2 -1
- package/src/utils/applications.test.ts +38 -1
- package/src/utils/applications.ts +204 -9
- package/src/utils/platform.ts +4 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { provenanceSource, HEALTH_META, type AppWorkload } from '../../utils/applications'
|
|
1
|
+
import { provenanceSource, HEALTH_META, appSourceLabel, isDeclaredAppSource, APP_IDENTITY_ANNOTATION, type AppWorkload } from '../../utils/applications'
|
|
2
2
|
import { midTruncate } from '../../utils/format'
|
|
3
3
|
|
|
4
4
|
// Structured tooltip content for the application chips. Both render a short
|
|
@@ -105,26 +105,53 @@ export function CategoryTooltip({
|
|
|
105
105
|
)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// AppIdentityTooltip — why these instances are grouped
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
108
|
+
// AppIdentityTooltip — why these instances are grouped, and (in the fleet view)
|
|
109
|
+
// whether the grouping folds across clusters + how to make it. A clear
|
|
110
|
+
// source-driven headline, the per-evidence detail lines, and — when `fleet` and
|
|
111
|
+
// the identity isn't a declared cross-cluster origin — the canonical "how to
|
|
112
|
+
// fold" answer, so the question and its answer share one surface.
|
|
113
|
+
//
|
|
114
|
+
// `source`/`portable` come from the row's identity; absent (an older host that
|
|
115
|
+
// doesn't pass them) the headline degrades to the evidence lines. `fleet` gates
|
|
116
|
+
// the cross-cluster "how to fold" line — single-cluster (OSS) has no clusters to
|
|
117
|
+
// fold across, so it shows only the grouping evidence.
|
|
112
118
|
export function AppIdentityTooltip({
|
|
113
119
|
members,
|
|
120
|
+
source,
|
|
121
|
+
portable,
|
|
122
|
+
fleet,
|
|
114
123
|
}: {
|
|
115
124
|
identityKey?: string
|
|
116
125
|
members: { name: string; env: string; confidence: string; evidence: string }[]
|
|
126
|
+
source?: string
|
|
127
|
+
portable?: boolean
|
|
128
|
+
fleet?: boolean
|
|
117
129
|
}) {
|
|
118
130
|
const evidences = Array.from(new Set(members.map((m) => m.evidence).filter(Boolean)))
|
|
119
131
|
const heuristicOnly = members.every((m) => m.confidence !== 'high')
|
|
132
|
+
// The wire `portable` flag is authoritative; fall back to deriving it from the
|
|
133
|
+
// source when an older host doesn't pass it.
|
|
134
|
+
const folds = portable ?? isDeclaredAppSource(source)
|
|
120
135
|
return (
|
|
121
|
-
<div className="max-w-xs space-y-1">
|
|
136
|
+
<div className="max-w-xs space-y-1.5">
|
|
137
|
+
{source && <div className="text-xs font-medium text-theme-text-primary">Grouped by {appSourceLabel(source)}.</div>}
|
|
122
138
|
{evidences.map((e, i) => (
|
|
123
|
-
<div key={i} className="text-xs text-theme-text-
|
|
139
|
+
<div key={i} className="text-xs text-theme-text-secondary">
|
|
124
140
|
<EvidenceLine evidence={e} />
|
|
125
141
|
</div>
|
|
126
142
|
))}
|
|
127
|
-
{
|
|
143
|
+
{fleet && folds && (
|
|
144
|
+
<div className="text-[10px] uppercase tracking-wide text-theme-text-tertiary">folds across clusters</div>
|
|
145
|
+
)}
|
|
146
|
+
{fleet && source !== undefined && !folds && (
|
|
147
|
+
<div className="space-y-1 border-t border-theme-border pt-1.5">
|
|
148
|
+
<div className="text-[11px] leading-snug text-theme-text-secondary">
|
|
149
|
+
Shown per-cluster — a name can't prove two clusters run the same app. To fold it across clusters, set the same{' '}
|
|
150
|
+
<code className="inline-code">{APP_IDENTITY_ANNOTATION}</code> on its workloads in each cluster, or deploy it via Argo CD / Flux with the environment in the source path.
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
{!fleet && heuristicOnly && !source && (
|
|
128
155
|
<div className={`text-[10px] uppercase tracking-wide ${HEALTH_META.degraded.text}`}>heuristic · medium confidence</div>
|
|
129
156
|
)}
|
|
130
157
|
</div>
|
|
@@ -93,6 +93,13 @@ export type ApplicationDetailProps = {
|
|
|
93
93
|
/** Env tokens the cluster proved (the list derives the same set) — keeps a
|
|
94
94
|
* ungrouped app's Environment fact consistent between list and detail. */
|
|
95
95
|
discoveredEnvs?: ReadonlySet<string>
|
|
96
|
+
/** Which instance in `identityInstances` is active, by its `appKey`. Defaults
|
|
97
|
+
* to `app.key` — correct for the OSS case, where each instance IS a distinct
|
|
98
|
+
* app row keyed the same way. A host that keys instances on something other
|
|
99
|
+
* than the app key (e.g. the Cloud fleet keys per cluster, while `app.key`
|
|
100
|
+
* must stay the logical app key for provenance) passes the active instance's
|
|
101
|
+
* key here so the switcher marks it. */
|
|
102
|
+
activeInstanceKey?: string
|
|
96
103
|
} & SelectionProps
|
|
97
104
|
|
|
98
105
|
function ContextFact({ label, children }: { label: string; children: ReactNode }) {
|
|
@@ -132,7 +139,7 @@ function compareDefinedVersions(a: string | undefined, b: string | undefined): n
|
|
|
132
139
|
return compareVersions(a, b) ?? 0
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
export function ApplicationDetail({ app, onBack, renderWorkload, topology, topologyLoading, onNavigateToResource, identityInstances, onSwitchInstance, discoveredEnvs, selectedWorkloadKey, onSelectWorkload }: ApplicationDetailProps) {
|
|
142
|
+
export function ApplicationDetail({ app, onBack, renderWorkload, topology, topologyLoading, onNavigateToResource, identityInstances, onSwitchInstance, discoveredEnvs, activeInstanceKey, selectedWorkloadKey, onSelectWorkload }: ApplicationDetailProps) {
|
|
136
143
|
// Stable order regardless of API ordering: rail rows and the per-workload
|
|
137
144
|
// color assignment both follow this array, so an order flap between
|
|
138
145
|
// refetches must not reshuffle rows or reassign a workload's hue.
|
|
@@ -286,7 +293,7 @@ export function ApplicationDetail({ app, onBack, renderWorkload, topology, topol
|
|
|
286
293
|
// pills for a handful; a picker beyond that (scales to ~any count).
|
|
287
294
|
<div className="flex min-w-0 items-center gap-1.5">
|
|
288
295
|
<span className="text-[10px] uppercase tracking-wide text-theme-text-tertiary">Environment</span>
|
|
289
|
-
<EnvSwitcher identityKey={app.identity?.key ?? ''} instances={identityInstances} activeKey={app.key} onSwitch={onSwitchInstance} />
|
|
296
|
+
<EnvSwitcher identityKey={app.identity?.key ?? ''} instances={identityInstances} activeKey={activeInstanceKey ?? app.key} onSwitch={onSwitchInstance} />
|
|
290
297
|
</div>
|
|
291
298
|
) : env ? (
|
|
292
299
|
<ContextFact label="Environment">
|