kubechart 0.1.0 → 0.2.0
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 +17 -1
- package/dist/cli.js +68 -9
- package/dist/cli.js.map +1 -1
- package/dist/graph/builder.d.ts +19 -0
- package/dist/graph/builder.d.ts.map +1 -0
- package/dist/graph/builder.js +140 -0
- package/dist/graph/builder.js.map +1 -0
- package/dist/output/serializer.d.ts +42 -0
- package/dist/output/serializer.d.ts.map +1 -0
- package/dist/output/serializer.js +42 -0
- package/dist/output/serializer.js.map +1 -0
- package/dist/render/BlockView.d.ts +8 -0
- package/dist/render/BlockView.d.ts.map +1 -0
- package/dist/render/BlockView.js +53 -0
- package/dist/render/BlockView.js.map +1 -0
- package/dist/render/GraphNode.d.ts +8 -0
- package/dist/render/GraphNode.d.ts.map +1 -0
- package/dist/render/GraphNode.js +48 -0
- package/dist/render/GraphNode.js.map +1 -0
- package/dist/render/GraphView.d.ts +8 -0
- package/dist/render/GraphView.d.ts.map +1 -0
- package/dist/render/GraphView.js +221 -0
- package/dist/render/GraphView.js.map +1 -0
- package/dist/render/NamespaceBlock.d.ts +9 -0
- package/dist/render/NamespaceBlock.d.ts.map +1 -0
- package/dist/render/NamespaceBlock.js +84 -0
- package/dist/render/NamespaceBlock.js.map +1 -0
- package/dist/render/StatusBar.d.ts +12 -0
- package/dist/render/StatusBar.d.ts.map +1 -0
- package/dist/render/StatusBar.js +43 -0
- package/dist/render/StatusBar.js.map +1 -0
- package/dist/render/TreeView.d.ts +2 -1
- package/dist/render/TreeView.d.ts.map +1 -1
- package/dist/render/TreeView.js +13 -12
- package/dist/render/TreeView.js.map +1 -1
- package/dist/render/WatchView.d.ts +12 -0
- package/dist/render/WatchView.d.ts.map +1 -0
- package/dist/render/WatchView.js +92 -0
- package/dist/render/WatchView.js.map +1 -0
- package/dist/render/WorkloadBlock.d.ts +9 -0
- package/dist/render/WorkloadBlock.d.ts.map +1 -0
- package/dist/render/WorkloadBlock.js +47 -0
- package/dist/render/WorkloadBlock.js.map +1 -0
- package/dist/render/ascii/box.d.ts +8 -0
- package/dist/render/ascii/box.d.ts.map +1 -0
- package/dist/render/ascii/box.js +25 -0
- package/dist/render/ascii/box.js.map +1 -0
- package/dist/render/colors.d.ts +1 -0
- package/dist/render/colors.d.ts.map +1 -1
- package/dist/render/colors.js +1 -0
- package/dist/render/colors.js.map +1 -1
- package/dist/watch/differ.d.ts +8 -0
- package/dist/watch/differ.d.ts.map +1 -0
- package/dist/watch/differ.js +113 -0
- package/dist/watch/differ.js.map +1 -0
- package/dist/watch/flash.d.ts +2 -0
- package/dist/watch/flash.d.ts.map +1 -0
- package/dist/watch/flash.js +13 -0
- package/dist/watch/flash.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ CLI tool to visualize Kubernetes cluster as an ASCII tree directly in your termi
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- **Watch Mode (Default)**: Auto-refresh with countdown timer, diff highlighting, and manual refresh with `r` key
|
|
7
8
|
- **ASCII Tree Visualization**: Clean, readable tree structure showing namespaces, workloads, pods, services, and ingresses
|
|
8
9
|
- **Color-Coded Status**: Visual indicators for pod health (Running, Pending, Failed, etc.)
|
|
9
10
|
- **Resource Type Symbols**: Distinct symbols for Deployments, StatefulSets, DaemonSets, Jobs, CronJobs, Services, and Ingresses
|
|
@@ -28,9 +29,12 @@ npx kubechart
|
|
|
28
29
|
### Basic Usage
|
|
29
30
|
|
|
30
31
|
```bash
|
|
31
|
-
#
|
|
32
|
+
# Watch mode (default) - auto-refresh every 5s
|
|
32
33
|
kubechart
|
|
33
34
|
|
|
35
|
+
# Print once and exit
|
|
36
|
+
kubechart --once
|
|
37
|
+
|
|
34
38
|
# Show all namespaces
|
|
35
39
|
kubechart -A
|
|
36
40
|
|
|
@@ -38,6 +42,18 @@ kubechart -A
|
|
|
38
42
|
kubechart -n production
|
|
39
43
|
```
|
|
40
44
|
|
|
45
|
+
### Watch Mode
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Watch with default interval (5s)
|
|
49
|
+
kubechart
|
|
50
|
+
|
|
51
|
+
# Custom refresh interval
|
|
52
|
+
kubechart --interval 10
|
|
53
|
+
|
|
54
|
+
# Manual refresh with 'r' key, quit with 'q' or Ctrl+C
|
|
55
|
+
```
|
|
56
|
+
|
|
41
57
|
### Filtering Options
|
|
42
58
|
|
|
43
59
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,13 @@ import { createClient } from './k8s/client.js';
|
|
|
6
6
|
import { fetchClusterData } from './k8s/fetcher.js';
|
|
7
7
|
import { buildTree } from './tree/builder.js';
|
|
8
8
|
import { TreeView } from './render/TreeView.js';
|
|
9
|
+
import { BlockView } from './render/BlockView.js';
|
|
10
|
+
import { GraphView } from './render/GraphView.js';
|
|
11
|
+
import { WatchView } from './render/WatchView.js';
|
|
9
12
|
import { setUseColors } from './render/colors.js';
|
|
13
|
+
import { serializeCluster } from './output/serializer.js';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as yaml from 'js-yaml';
|
|
10
16
|
const program = new Command();
|
|
11
17
|
program
|
|
12
18
|
.name('kubechart')
|
|
@@ -18,6 +24,11 @@ program
|
|
|
18
24
|
.option('-l, --selector <sel>', 'Label selector (e.g. app=api,env=prod)')
|
|
19
25
|
.option('--show-errors', 'Only show workloads with errors')
|
|
20
26
|
.option('--no-color', 'Disable colored output')
|
|
27
|
+
.option('--once', 'Print chart once and exit (default: watch mode)')
|
|
28
|
+
.option('--interval <seconds>', 'Watch refresh interval (default: 5)', '5')
|
|
29
|
+
.option('--view <mode>', 'View mode: tree | block | graph (default: tree)', 'tree')
|
|
30
|
+
.option('--output <format>', 'Output format: json | yaml (requires --out-file)')
|
|
31
|
+
.option('--out-file <path>', 'File path to write output (requires --output)')
|
|
21
32
|
.parse(process.argv);
|
|
22
33
|
const options = program.opts();
|
|
23
34
|
async function main() {
|
|
@@ -35,15 +46,63 @@ async function main() {
|
|
|
35
46
|
selector: options.selector,
|
|
36
47
|
showErrors: options.showErrors,
|
|
37
48
|
};
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
// Static mode (--once flag)
|
|
50
|
+
if (options.once) {
|
|
51
|
+
const rawData = await fetchClusterData(client, fetchOpts);
|
|
52
|
+
const tree = buildTree(rawData, client.contextName, {
|
|
53
|
+
showErrors: options.showErrors,
|
|
54
|
+
selector: options.selector,
|
|
55
|
+
});
|
|
56
|
+
// Output to file
|
|
57
|
+
if (options.output && options.outFile) {
|
|
58
|
+
const snapshot = serializeCluster(tree);
|
|
59
|
+
let content;
|
|
60
|
+
if (options.output === 'json') {
|
|
61
|
+
content = JSON.stringify(snapshot, null, 2);
|
|
62
|
+
}
|
|
63
|
+
else if (options.output === 'yaml') {
|
|
64
|
+
content = yaml.dump(snapshot);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.error(`Error: --output must be 'json' or 'yaml'`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
fs.writeFileSync(options.outFile, content, 'utf-8');
|
|
71
|
+
console.log(`Output written to ${options.outFile}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (options.output || options.outFile) {
|
|
75
|
+
console.error('Error: --output and --out-file must be used together');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
// Select view component based on --view flag
|
|
79
|
+
let ViewComponent;
|
|
80
|
+
if (options.view === 'block') {
|
|
81
|
+
ViewComponent = BlockView;
|
|
82
|
+
}
|
|
83
|
+
else if (options.view === 'graph') {
|
|
84
|
+
ViewComponent = GraphView;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
ViewComponent = TreeView;
|
|
88
|
+
}
|
|
89
|
+
const { waitUntilExit } = render(React.createElement(ViewComponent, { tree }));
|
|
90
|
+
await waitUntilExit();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Watch mode (default)
|
|
94
|
+
const interval = parseInt(options.interval, 10);
|
|
95
|
+
if (isNaN(interval) || interval < 1) {
|
|
96
|
+
console.error('Error: --interval must be a positive number');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const { waitUntilExit } = render(React.createElement(WatchView, {
|
|
100
|
+
opts: {
|
|
101
|
+
interval,
|
|
102
|
+
fetchOpts,
|
|
103
|
+
client,
|
|
104
|
+
},
|
|
105
|
+
}));
|
|
47
106
|
await waitUntilExit();
|
|
48
107
|
}
|
|
49
108
|
catch (error) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NamespaceNode, PodPhase } from '../tree/types.js';
|
|
2
|
+
export interface GraphNode {
|
|
3
|
+
id: string;
|
|
4
|
+
kind: 'Internet' | 'Ingress' | 'Service' | 'Workload' | 'Pod';
|
|
5
|
+
label: string;
|
|
6
|
+
meta: string[];
|
|
7
|
+
status?: PodPhase;
|
|
8
|
+
}
|
|
9
|
+
export interface GraphEdge {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface Graph {
|
|
15
|
+
nodes: GraphNode[];
|
|
16
|
+
edges: GraphEdge[];
|
|
17
|
+
}
|
|
18
|
+
export declare function buildGraph(ns: NamespaceNode): Graph;
|
|
19
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/graph/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,aAAa,GAAG,KAAK,CAqInD"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export function buildGraph(ns) {
|
|
2
|
+
const nodes = [];
|
|
3
|
+
const edges = [];
|
|
4
|
+
const nodeIds = new Set();
|
|
5
|
+
// Helper to add node if not exists
|
|
6
|
+
const addNode = (node) => {
|
|
7
|
+
if (!nodeIds.has(node.id)) {
|
|
8
|
+
nodes.push(node);
|
|
9
|
+
nodeIds.add(node.id);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
// Helper to add edge
|
|
13
|
+
const addEdge = (edge) => {
|
|
14
|
+
edges.push(edge);
|
|
15
|
+
};
|
|
16
|
+
// Start with Internet node
|
|
17
|
+
addNode({
|
|
18
|
+
id: 'internet',
|
|
19
|
+
kind: 'Internet',
|
|
20
|
+
label: 'Internet',
|
|
21
|
+
meta: [],
|
|
22
|
+
});
|
|
23
|
+
// Process Ingresses
|
|
24
|
+
for (const ingress of ns.ingresses) {
|
|
25
|
+
const ingressId = `ingress-${ingress.name}`;
|
|
26
|
+
addNode({
|
|
27
|
+
id: ingressId,
|
|
28
|
+
kind: 'Ingress',
|
|
29
|
+
label: `Ingress`,
|
|
30
|
+
meta: [ingress.host, ingress.tls ? 'TLS: ✓' : 'TLS: ✗'],
|
|
31
|
+
});
|
|
32
|
+
// Edge from Internet to Ingress
|
|
33
|
+
addEdge({
|
|
34
|
+
from: 'internet',
|
|
35
|
+
to: ingressId,
|
|
36
|
+
label: ingress.host,
|
|
37
|
+
});
|
|
38
|
+
// Find target service from ingress paths
|
|
39
|
+
for (const path of ingress.paths) {
|
|
40
|
+
// Extract service name from path (simplified - assumes format like /path -> service:port)
|
|
41
|
+
const serviceName = path.split(':')[0] || path;
|
|
42
|
+
const targetService = ns.services.find((s) => s.name === serviceName);
|
|
43
|
+
if (targetService) {
|
|
44
|
+
const serviceId = `service-${targetService.name}`;
|
|
45
|
+
addNode({
|
|
46
|
+
id: serviceId,
|
|
47
|
+
kind: 'Service',
|
|
48
|
+
label: `Service: ${targetService.name}`,
|
|
49
|
+
meta: [
|
|
50
|
+
`${targetService.type} ${targetService.clusterIP}`,
|
|
51
|
+
`port: ${targetService.ports.join(', ')}`,
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
addEdge({
|
|
55
|
+
from: ingressId,
|
|
56
|
+
to: serviceId,
|
|
57
|
+
label: `→ ${targetService.name}:${targetService.ports[0] || '80'}`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Process Services (including those not linked to Ingress)
|
|
63
|
+
for (const service of ns.services) {
|
|
64
|
+
const serviceId = `service-${service.name}`;
|
|
65
|
+
if (!nodeIds.has(serviceId)) {
|
|
66
|
+
addNode({
|
|
67
|
+
id: serviceId,
|
|
68
|
+
kind: 'Service',
|
|
69
|
+
label: `Service: ${service.name}`,
|
|
70
|
+
meta: [
|
|
71
|
+
`${service.type} ${service.clusterIP}`,
|
|
72
|
+
`port: ${service.ports.join(', ')}`,
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Find pods that match this service's selector
|
|
77
|
+
// For simplicity, we'll link all pods in the namespace to all services
|
|
78
|
+
// In a real implementation, this would use label selectors
|
|
79
|
+
for (const workload of ns.workloads) {
|
|
80
|
+
for (const pod of workload.pods) {
|
|
81
|
+
const podId = `pod-${pod.name}`;
|
|
82
|
+
addNode({
|
|
83
|
+
id: podId,
|
|
84
|
+
kind: 'Pod',
|
|
85
|
+
label: pod.name,
|
|
86
|
+
meta: [
|
|
87
|
+
`${getPodStatusSymbol(pod.phase, pod.ready)} ${pod.phase}`,
|
|
88
|
+
`node: ${pod.nodeName}`,
|
|
89
|
+
pod.restarts > 0 ? `restarts: ${pod.restarts}` : '',
|
|
90
|
+
].filter(Boolean),
|
|
91
|
+
status: pod.phase,
|
|
92
|
+
});
|
|
93
|
+
addEdge({
|
|
94
|
+
from: serviceId,
|
|
95
|
+
to: podId,
|
|
96
|
+
label: `load balances to`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Process workloads (as grouping for pods)
|
|
102
|
+
for (const workload of ns.workloads) {
|
|
103
|
+
const workloadId = `workload-${workload.name}`;
|
|
104
|
+
addNode({
|
|
105
|
+
id: workloadId,
|
|
106
|
+
kind: 'Workload',
|
|
107
|
+
label: `${workload.kind}: ${workload.name}`,
|
|
108
|
+
meta: [`ready: ${workload.ready}`, `image: ${workload.image}`],
|
|
109
|
+
});
|
|
110
|
+
// Link pods to workload
|
|
111
|
+
for (const pod of workload.pods) {
|
|
112
|
+
const podId = `pod-${pod.name}`;
|
|
113
|
+
addEdge({
|
|
114
|
+
from: podId,
|
|
115
|
+
to: workloadId,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { nodes, edges };
|
|
120
|
+
}
|
|
121
|
+
function getPodStatusSymbol(phase, ready) {
|
|
122
|
+
if (phase === 'Running') {
|
|
123
|
+
const [readyContainers, totalContainers] = ready.split('/').map(Number);
|
|
124
|
+
if (readyContainers < totalContainers) {
|
|
125
|
+
return '◑';
|
|
126
|
+
}
|
|
127
|
+
return '●';
|
|
128
|
+
}
|
|
129
|
+
switch (phase) {
|
|
130
|
+
case 'Pending':
|
|
131
|
+
return '◌';
|
|
132
|
+
case 'Failed':
|
|
133
|
+
return '✖';
|
|
134
|
+
case 'Succeeded':
|
|
135
|
+
return '○';
|
|
136
|
+
default:
|
|
137
|
+
return '?';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/graph/builder.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,UAAU,CAAC,EAAiB;IAC1C,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,mCAAmC;IACnC,MAAM,OAAO,GAAG,CAAC,IAAe,EAAE,EAAE;QAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,qBAAqB;IACrB,MAAM,OAAO,GAAG,CAAC,IAAe,EAAE,EAAE;QAClC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,2BAA2B;IAC3B,OAAO,CAAC;QACN,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IAEH,oBAAoB;IACpB,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5C,OAAO,CAAC;YACN,EAAE,EAAE,SAAS;YACb,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;SACxD,CAAC,CAAC;QAEH,gCAAgC;QAChC,OAAO,CAAC;YACN,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,SAAS;YACb,KAAK,EAAE,OAAO,CAAC,IAAI;SACpB,CAAC,CAAC;QAEH,yCAAyC;QACzC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,0FAA0F;YAC1F,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;YAC/C,MAAM,aAAa,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;YAEtE,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,SAAS,GAAG,WAAW,aAAa,CAAC,IAAI,EAAE,CAAC;gBAClD,OAAO,CAAC;oBACN,EAAE,EAAE,SAAS;oBACb,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE,YAAY,aAAa,CAAC,IAAI,EAAE;oBACvC,IAAI,EAAE;wBACJ,GAAG,aAAa,CAAC,IAAI,IAAI,aAAa,CAAC,SAAS,EAAE;wBAClD,SAAS,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;qBAC1C;iBACF,CAAC,CAAC;gBAEH,OAAO,CAAC;oBACN,IAAI,EAAE,SAAS;oBACf,EAAE,EAAE,SAAS;oBACb,KAAK,EAAE,KAAK,aAAa,CAAC,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;iBACnE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC;gBACN,EAAE,EAAE,SAAS;gBACb,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,YAAY,OAAO,CAAC,IAAI,EAAE;gBACjC,IAAI,EAAE;oBACJ,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE;oBACtC,SAAS,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBACpC;aACF,CAAC,CAAC;QACL,CAAC;QAED,+CAA+C;QAC/C,uEAAuE;QACvE,2DAA2D;QAC3D,KAAK,MAAM,QAAQ,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;YACpC,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,OAAO,CAAC;oBACN,EAAE,EAAE,KAAK;oBACT,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,GAAG,CAAC,IAAI;oBACf,IAAI,EAAE;wBACJ,GAAG,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE;wBAC1D,SAAS,GAAG,CAAC,QAAQ,EAAE;wBACvB,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;qBACpD,CAAC,MAAM,CAAC,OAAO,CAAC;oBACjB,MAAM,EAAE,GAAG,CAAC,KAAK;iBAClB,CAAC,CAAC;gBAEH,OAAO,CAAC;oBACN,IAAI,EAAE,SAAS;oBACf,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,kBAAkB;iBAC1B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,KAAK,MAAM,QAAQ,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,YAAY,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/C,OAAO,CAAC;YACN,EAAE,EAAE,UAAU;YACd,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,GAAG,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,EAAE;YAC3C,IAAI,EAAE,CAAC,UAAU,QAAQ,CAAC,KAAK,EAAE,EAAE,UAAU,QAAQ,CAAC,KAAK,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,wBAAwB;QACxB,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;YAChC,OAAO,CAAC;gBACN,IAAI,EAAE,KAAK;gBACX,EAAE,EAAE,UAAU;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa,EAAE,KAAa;IACtD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxE,IAAI,eAAe,GAAG,eAAe,EAAE,CAAC;YACtC,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,GAAG,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,GAAG,CAAC;QACb,KAAK,WAAW;YACd,OAAO,GAAG,CAAC;QACb;YACE,OAAO,GAAG,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ClusterTree } from '../tree/types.js';
|
|
2
|
+
export interface ClusterSnapshot {
|
|
3
|
+
cluster: {
|
|
4
|
+
contextName: string;
|
|
5
|
+
serverVersion: string;
|
|
6
|
+
nodeCount: number;
|
|
7
|
+
fetchedAt: string;
|
|
8
|
+
};
|
|
9
|
+
namespaces: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
status: string;
|
|
12
|
+
workloads: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
ready: string;
|
|
16
|
+
image: string;
|
|
17
|
+
pods: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
phase: string;
|
|
20
|
+
nodeName: string;
|
|
21
|
+
ip: string;
|
|
22
|
+
restarts: number;
|
|
23
|
+
reason?: string;
|
|
24
|
+
ready: string;
|
|
25
|
+
}>;
|
|
26
|
+
}>;
|
|
27
|
+
services: Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
type: string;
|
|
30
|
+
clusterIP: string;
|
|
31
|
+
ports: string[];
|
|
32
|
+
}>;
|
|
33
|
+
ingresses: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
host: string;
|
|
36
|
+
paths: string[];
|
|
37
|
+
tls: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export declare function serializeCluster(tree: ClusterTree): ClusterSnapshot;
|
|
42
|
+
//# sourceMappingURL=serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../src/output/serializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,KAAK,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,KAAK,CAAC;gBACV,IAAI,EAAE,MAAM,CAAC;gBACb,KAAK,EAAE,MAAM,CAAC;gBACd,QAAQ,EAAE,MAAM,CAAC;gBACjB,EAAE,EAAE,MAAM,CAAC;gBACX,QAAQ,EAAE,MAAM,CAAC;gBACjB,MAAM,CAAC,EAAE,MAAM,CAAC;gBAChB,KAAK,EAAE,MAAM,CAAC;aACf,CAAC,CAAC;SACJ,CAAC,CAAC;QACH,QAAQ,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,EAAE,MAAM,EAAE,CAAC;SACjB,CAAC,CAAC;QACH,SAAS,EAAE,KAAK,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,GAAG,EAAE,OAAO,CAAC;SACd,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAwCnE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function serializeCluster(tree) {
|
|
2
|
+
return {
|
|
3
|
+
cluster: {
|
|
4
|
+
contextName: tree.contextName,
|
|
5
|
+
serverVersion: tree.serverVersion,
|
|
6
|
+
nodeCount: tree.nodeCount,
|
|
7
|
+
fetchedAt: tree.fetchedAt.toISOString(),
|
|
8
|
+
},
|
|
9
|
+
namespaces: tree.namespaces.map((ns) => ({
|
|
10
|
+
name: ns.name,
|
|
11
|
+
status: ns.status,
|
|
12
|
+
workloads: ns.workloads.map((wl) => ({
|
|
13
|
+
name: wl.name,
|
|
14
|
+
kind: wl.kind,
|
|
15
|
+
ready: wl.ready,
|
|
16
|
+
image: wl.image,
|
|
17
|
+
pods: wl.pods.map((pod) => ({
|
|
18
|
+
name: pod.name,
|
|
19
|
+
phase: pod.phase,
|
|
20
|
+
nodeName: pod.nodeName,
|
|
21
|
+
ip: pod.ip,
|
|
22
|
+
restarts: pod.restarts,
|
|
23
|
+
reason: pod.reason,
|
|
24
|
+
ready: pod.ready,
|
|
25
|
+
})),
|
|
26
|
+
})),
|
|
27
|
+
services: ns.services.map((svc) => ({
|
|
28
|
+
name: svc.name,
|
|
29
|
+
type: svc.type,
|
|
30
|
+
clusterIP: svc.clusterIP,
|
|
31
|
+
ports: svc.ports,
|
|
32
|
+
})),
|
|
33
|
+
ingresses: ns.ingresses.map((ing) => ({
|
|
34
|
+
name: ing.name,
|
|
35
|
+
host: ing.host,
|
|
36
|
+
paths: ing.paths,
|
|
37
|
+
tls: ing.tls,
|
|
38
|
+
})),
|
|
39
|
+
})),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/output/serializer.ts"],"names":[],"mappings":"AA0CA,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,OAAO;QACL,OAAO,EAAE;YACP,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;SACxC;QACD,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACnC,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;oBAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,EAAE,EAAE,GAAG,CAAC,EAAE;oBACV,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;iBACjB,CAAC,CAAC;aACJ,CAAC,CAAC;YACH,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAClC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,KAAK,EAAE,GAAG,CAAC,KAAK;aACjB,CAAC,CAAC;YACH,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACpC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;aACb,CAAC,CAAC;SACJ,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ClusterTree } from '../tree/types.js';
|
|
3
|
+
interface BlockViewProps {
|
|
4
|
+
tree: ClusterTree;
|
|
5
|
+
}
|
|
6
|
+
export declare function BlockView({ tree }: BlockViewProps): React.ReactElement;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=BlockView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockView.d.ts","sourceRoot":"","sources":["../../src/render/BlockView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,UAAU,cAAc;IACtB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CA2DtE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { NamespaceBlock } from './NamespaceBlock.js';
|
|
4
|
+
export function BlockView({ tree }) {
|
|
5
|
+
const [terminalWidth, setTerminalWidth] = useState(process.stdout.columns || 80);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const handleResize = () => {
|
|
8
|
+
setTerminalWidth(process.stdout.columns || 80);
|
|
9
|
+
};
|
|
10
|
+
process.stdout.on('resize', handleResize);
|
|
11
|
+
return () => {
|
|
12
|
+
process.stdout.off('resize', handleResize);
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
// Calculate stats
|
|
16
|
+
const totalNamespaces = tree.namespaces.length;
|
|
17
|
+
const totalWorkloads = tree.namespaces.reduce((sum, ns) => sum + ns.workloads.length, 0);
|
|
18
|
+
const totalPods = tree.namespaces.reduce((sum, ns) => sum + ns.workloads.reduce((pSum, wl) => pSum + wl.pods.length, 0), 0);
|
|
19
|
+
const totalServices = tree.namespaces.reduce((sum, ns) => sum + ns.services.length, 0);
|
|
20
|
+
const totalIngresses = tree.namespaces.reduce((sum, ns) => sum + ns.ingresses.length, 0);
|
|
21
|
+
// Count errors (pods with Failed phase or non-zero restarts)
|
|
22
|
+
const totalErrors = tree.namespaces.reduce((sum, ns) => sum +
|
|
23
|
+
ns.workloads.reduce((wlSum, wl) => wlSum +
|
|
24
|
+
wl.pods.filter((pod) => pod.phase === 'Failed' || pod.restarts > 0).length, 0), 0);
|
|
25
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
26
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
27
|
+
React.createElement(Text, null,
|
|
28
|
+
"CLUSTER: ",
|
|
29
|
+
tree.contextName,
|
|
30
|
+
" | k8s ",
|
|
31
|
+
tree.serverVersion,
|
|
32
|
+
" | ",
|
|
33
|
+
tree.nodeCount,
|
|
34
|
+
" nodes")),
|
|
35
|
+
tree.namespaces.map((ns) => (React.createElement(NamespaceBlock, { key: ns.name, namespace: ns, terminalWidth: terminalWidth }))),
|
|
36
|
+
React.createElement(Box, { marginTop: 1 },
|
|
37
|
+
React.createElement(Text, null, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),
|
|
38
|
+
React.createElement(Box, null,
|
|
39
|
+
React.createElement(Text, null,
|
|
40
|
+
"namespaces: ",
|
|
41
|
+
totalNamespaces,
|
|
42
|
+
" | workloads: ",
|
|
43
|
+
totalWorkloads,
|
|
44
|
+
" | pods: ",
|
|
45
|
+
totalPods,
|
|
46
|
+
" | services: ",
|
|
47
|
+
totalServices,
|
|
48
|
+
" | ingresses: ",
|
|
49
|
+
totalIngresses,
|
|
50
|
+
" | errors: ",
|
|
51
|
+
totalErrors))));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=BlockView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockView.js","sourceRoot":"","sources":["../../src/render/BlockView.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { GraphNode } from '../graph/builder.js';
|
|
3
|
+
interface GraphNodeProps {
|
|
4
|
+
node: GraphNode;
|
|
5
|
+
}
|
|
6
|
+
export declare function GraphNodeComponent({ node }: GraphNodeProps): React.ReactElement;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=GraphNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphNode.d.ts","sourceRoot":"","sources":["../../src/render/GraphNode.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,UAAU,cAAc;IACtB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAgD/E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export function GraphNodeComponent({ node }) {
|
|
4
|
+
const getStatusColor = () => {
|
|
5
|
+
if (node.kind === 'Pod' && node.status) {
|
|
6
|
+
switch (node.status) {
|
|
7
|
+
case 'Running':
|
|
8
|
+
return 'green';
|
|
9
|
+
case 'Pending':
|
|
10
|
+
return 'yellow';
|
|
11
|
+
case 'Failed':
|
|
12
|
+
return 'red';
|
|
13
|
+
case 'Succeeded':
|
|
14
|
+
return 'gray';
|
|
15
|
+
default:
|
|
16
|
+
return 'white';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return 'white';
|
|
20
|
+
};
|
|
21
|
+
const getKindSymbol = () => {
|
|
22
|
+
switch (node.kind) {
|
|
23
|
+
case 'Internet':
|
|
24
|
+
return '☁';
|
|
25
|
+
case 'Ingress':
|
|
26
|
+
return '◆';
|
|
27
|
+
case 'Service':
|
|
28
|
+
return '●';
|
|
29
|
+
case 'Workload':
|
|
30
|
+
return '▲';
|
|
31
|
+
case 'Pod':
|
|
32
|
+
return '○';
|
|
33
|
+
default:
|
|
34
|
+
return '▪';
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
38
|
+
React.createElement(Box, null,
|
|
39
|
+
React.createElement(Text, { color: getStatusColor() },
|
|
40
|
+
getKindSymbol(),
|
|
41
|
+
" ",
|
|
42
|
+
node.label)),
|
|
43
|
+
node.meta.map((line, index) => (React.createElement(Box, { key: index },
|
|
44
|
+
React.createElement(Text, { dimColor: true },
|
|
45
|
+
" ",
|
|
46
|
+
line))))));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=GraphNode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphNode.js","sourceRoot":"","sources":["../../src/render/GraphNode.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAOhC,MAAM,UAAU,kBAAkB,CAAC,EAAE,IAAI,EAAkB;IACzD,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACvC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;gBACpB,KAAK,SAAS;oBACZ,OAAO,OAAO,CAAC;gBACjB,KAAK,SAAS;oBACZ,OAAO,QAAQ,CAAC;gBAClB,KAAK,QAAQ;oBACX,OAAO,KAAK,CAAC;gBACf,KAAK,WAAW;oBACd,OAAO,MAAM,CAAC;gBAChB;oBACE,OAAO,OAAO,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,UAAU;gBACb,OAAO,GAAG,CAAC;YACb,KAAK,SAAS;gBACZ,OAAO,GAAG,CAAC;YACb,KAAK,SAAS;gBACZ,OAAO,GAAG,CAAC;YACb,KAAK,UAAU;gBACb,OAAO,GAAG,CAAC;YACb,KAAK,KAAK;gBACR,OAAO,GAAG,CAAC;YACb;gBACE,OAAO,GAAG,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,GAAG;YACF,oBAAC,IAAI,IAAC,KAAK,EAAE,cAAc,EAAE;gBAAG,aAAa,EAAE;;gBAAG,IAAI,CAAC,KAAK,CAAQ,CAChE;QACL,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9B,oBAAC,GAAG,IAAC,GAAG,EAAE,KAAK;YACb,oBAAC,IAAI,IAAC,QAAQ;;gBAAI,IAAI,CAAQ,CAC1B,CACP,CAAC,CACE,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ClusterTree } from '../tree/types.js';
|
|
3
|
+
interface GraphViewProps {
|
|
4
|
+
tree: ClusterTree;
|
|
5
|
+
}
|
|
6
|
+
export declare function GraphView({ tree }: GraphViewProps): React.ReactElement;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=GraphView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../src/render/GraphView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,kBAAkB,CAAC;AAGnE,UAAU,cAAc;IACtB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAgBtE"}
|