pyre-cli 9.5.1 → 10.0.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 +84 -14
- package/dist/alertChannels-TPSQIJ7S.js +8 -0
- package/dist/blender-FGH3ASEQ.js +7 -0
- package/dist/brewHealth-P2GW5Y5C.js +176 -0
- package/dist/buildCmd-CDPZ2ZFA.js +38 -0
- package/dist/{checkCmd-EWFNLBWE.js → checkCmd-RKW6QGDR.js} +10 -11
- package/dist/{chunk-RVGKEJXT.js → chunk-53O2RGSL.js} +47 -17
- package/dist/{chunk-MOAEGMFP.js → chunk-6UHZP4QP.js} +204 -46
- package/dist/chunk-GHRDW646.js +2506 -0
- package/dist/{prometheus-LFYHF6VO.js → chunk-HT4VQ4OH.js} +1 -0
- package/dist/chunk-M73HCQD2.js +95 -0
- package/dist/chunk-MZO5QKUR.js +32 -0
- package/dist/chunk-VIACN2RU.js +133 -0
- package/dist/chunk-Z2YV5HKV.js +87 -0
- package/dist/chunk-ZLVIVFSO.js +110 -0
- package/dist/{collectors-WD6VKH6M.js → collectors-H43PN4PS.js} +8 -2
- package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
- package/dist/{config-4ZLF4HTS.js → config-GYGJXU2Q.js} +9 -1
- package/dist/doctor-GYHFTNZQ.js +387 -0
- package/dist/explainCmd-MF6PIMLW.js +222 -0
- package/dist/extensions-XJGHG7X3.js +134 -0
- package/dist/index.js +841 -206
- package/dist/ioreport.py +72 -0
- package/dist/netUsageCmd-WKBTW2YG.js +90 -0
- package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-7REDBLBU.js} +5 -3
- package/dist/profileProcCmd-N4H6MCBT.js +107 -0
- package/dist/prometheus-5BL5PVLC.js +6 -0
- package/dist/prometheusCmd-PL64BLN5.js +93 -0
- package/dist/replayCmd-Z3PYXY7V.js +164 -0
- package/dist/run-HOGQDDTA.js +8 -0
- package/dist/serveCmd-2H5VLF3P.js +153 -0
- package/dist/service-Q74BBILO.js +181 -0
- package/dist/smartCmd-ODWKSFNG.js +166 -0
- package/dist/syncCmd-A2W3QB3S.js +126 -0
- package/dist/topoCmd-DU2HHZNH.js +148 -0
- package/dist/watchdogCmd-UF53JPZB.js +161 -0
- package/dist/{wizard-GVIAHIVV.js → wizard-TD7BGX6O.js} +1 -1
- package/install.ps1 +29 -0
- package/install.sh +10 -9
- package/package.json +7 -3
- package/src/ai/models.ts +138 -0
- package/src/alertChannels.ts +119 -0
- package/src/anomalies.ts +114 -60
- package/src/brewHealth.ts +157 -40
- package/src/buildCmd.ts +48 -0
- package/src/checkCmd.ts +5 -5
- package/src/completions.ts +35 -0
- package/src/doctor.ts +235 -7
- package/src/easy_features.test.ts +40 -0
- package/src/explainCmd.ts +261 -0
- package/src/extensions.ts +71 -7
- package/src/formatters/index.ts +1 -1
- package/src/formatters/output.ts +100 -13
- package/src/formatters/render.ts +274 -38
- package/src/formatters/themes.ts +8 -0
- package/src/formatters/types.ts +6 -1
- package/src/index.ts +298 -56
- package/src/live/input.ts +122 -4
- package/src/live/render.ts +98 -0
- package/src/live/state.ts +70 -52
- package/src/live/types.ts +2 -2
- package/src/monitors/batteryPredictor.ts +143 -0
- package/src/monitors/blender.ts +144 -0
- package/src/monitors/buildTracker.ts +121 -0
- package/src/monitors/collectors.ts +558 -147
- package/src/monitors/dns_cache.ts +48 -0
- package/src/monitors/index.ts +3 -2
- package/src/monitors/ioreport.py +72 -0
- package/src/monitors/platform/linux.ts +756 -0
- package/src/monitors/platform/windows.ts +519 -0
- package/src/monitors/run.ts +27 -3
- package/src/monitors/smc.ts +21 -1
- package/src/monitors/types.ts +112 -14
- package/src/netUsageCmd.ts +121 -0
- package/src/platform.test.ts +99 -0
- package/src/plugins/index.ts +143 -0
- package/src/profileProcCmd.ts +133 -0
- package/src/prometheusCmd.ts +107 -0
- package/src/replayCmd.ts +186 -0
- package/src/serveCmd.ts +180 -0
- package/src/service.ts +125 -7
- package/src/smartCmd.ts +205 -0
- package/src/state/config.ts +64 -17
- package/src/syncCmd.ts +166 -0
- package/src/topoCmd.ts +176 -0
- package/src/watchdogCmd.ts +198 -0
- package/src/xbar.ts +22 -11
- package/dist/brewHealth-F2BXF5M6.js +0 -74
- package/dist/chunk-FH5WDJBF.js +0 -15
- package/dist/chunk-HGRBBPYX.js +0 -937
- package/dist/doctor-L6FZYA7F.js +0 -184
- package/dist/extensions-DVA5YLGH.js +0 -74
- package/dist/service-WTS3NBQU.js +0 -73
- /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
package/src/formatters/index.ts
CHANGED
|
@@ -15,4 +15,4 @@ export { formatTable, clampWidth, gridColumns, thermalColor, capacityColor, getT
|
|
|
15
15
|
export { formatJson, formatCsv, formatTsv, formatGraphs, formatHtml, formatMarkdown } from './output.js';
|
|
16
16
|
export { getDashboardHtml } from './html_template.js';
|
|
17
17
|
export type { ThemeName, ThemeColors, VisibleItems, TableOptions, AnomalyAlert } from './types.js';
|
|
18
|
-
export { THEMES } from './themes.js';
|
|
18
|
+
export { THEMES } from './themes.js';
|
package/src/formatters/output.ts
CHANGED
|
@@ -111,21 +111,38 @@ export function formatHtml(data: StatsData): string {
|
|
|
111
111
|
<table>
|
|
112
112
|
${rows([
|
|
113
113
|
{ label: 'Interface', value: `${data.network.interface} (${data.network.ip})` },
|
|
114
|
-
{ label: 'RX', value: `${formatBytes(data.network.rxBytes)}
|
|
115
|
-
{ label: 'TX', value: `${formatBytes(data.network.txBytes)}
|
|
114
|
+
{ label: 'RX', value: `${formatBytes(data.network.rxBytes)} · ${formatBytes(data.network.rxRate)}/s` },
|
|
115
|
+
{ label: 'TX', value: `${formatBytes(data.network.txBytes)} · ${formatBytes(data.network.txRate)}/s` },
|
|
116
|
+
...(data.network.protocols ? [
|
|
117
|
+
{ label: 'TCP', value: `${data.network.protocols.tcp.connections} conns` },
|
|
118
|
+
{ label: 'UDP', value: `${data.network.protocols.udp.connections} conns` },
|
|
119
|
+
] : []),
|
|
120
|
+
...(data.network.connectionStates ? [
|
|
121
|
+
{ label: 'Active', value: `${data.network.connectionStates.established} ESTABLISHED` },
|
|
122
|
+
{ label: 'Listening', value: `${data.network.connectionStates.listening}` },
|
|
123
|
+
] : []),
|
|
124
|
+
...(data.network.listeningPorts && data.network.listeningPorts.length ? [
|
|
125
|
+
{ label: 'Ports', value: data.network.listeningPorts.slice(0, 8).join(', ') },
|
|
126
|
+
] : []),
|
|
116
127
|
]).trim()}
|
|
117
128
|
</table>`);
|
|
118
129
|
|
|
119
130
|
if (data.packets) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
const packetRows: { label: string; value: string }[] = [
|
|
132
|
+
{ label: 'Total', value: `${data.packets.totalPackets} pkt` },
|
|
133
|
+
{ label: 'RX', value: `${data.packets.rxPackets} pkt` },
|
|
134
|
+
{ label: 'TX', value: `${data.packets.txPackets} pkt` },
|
|
135
|
+
{ label: 'Connections', value: `${data.packets.connections} TCP` },
|
|
136
|
+
];
|
|
137
|
+
if (data.packets.protocolStats) {
|
|
138
|
+
packetRows.push({ label: 'TCP', value: `${data.packets.protocolStats.tcp.connections} conns` });
|
|
139
|
+
packetRows.push({ label: 'UDP', value: `${data.packets.protocolStats.udp.connections} conns` });
|
|
140
|
+
}
|
|
141
|
+
if (data.packets.connectionStates) {
|
|
142
|
+
packetRows.push({ label: 'ESTABLISHED', value: `${data.packets.connectionStates.established}` });
|
|
143
|
+
packetRows.push({ label: 'LISTENING', value: `${data.packets.connectionStates.listening}` });
|
|
144
|
+
}
|
|
145
|
+
sections.push(`<h2>Packets</h2>\n<table>\n ${rows(packetRows).trim()}\n</table>`);
|
|
129
146
|
}
|
|
130
147
|
|
|
131
148
|
if (data.tasks.length) {
|
|
@@ -152,6 +169,14 @@ export function formatHtml(data: StatsData): string {
|
|
|
152
169
|
sections.push(`<h2>Processes</h2>\n<table>\n ${rows(procRows).trim()}\n</table>`);
|
|
153
170
|
}
|
|
154
171
|
|
|
172
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
173
|
+
const blenderRows = data.blenderRenders.map(r => ({
|
|
174
|
+
label: `PID ${r.pid}`,
|
|
175
|
+
value: `${r.blendFile} · ${r.renderEngine} · ${r.currentFrame}/${r.totalFrames} frames · ${r.completionPercent}% · ${r.status} · ${r.elapsedSec}s`,
|
|
176
|
+
}));
|
|
177
|
+
sections.push(`<h2>Blender Renders</h2>\n<table>\n ${rows(blenderRows).trim()}\n</table>`);
|
|
178
|
+
}
|
|
179
|
+
|
|
155
180
|
return `<!DOCTYPE html>
|
|
156
181
|
<html lang="en">
|
|
157
182
|
<head>
|
|
@@ -380,8 +405,19 @@ export function formatMarkdown(data: StatsData): string {
|
|
|
380
405
|
lines.push('| Property | Value |');
|
|
381
406
|
lines.push('|---|---|');
|
|
382
407
|
lines.push(`| Interface | ${mdCell(`${data.network.interface} (${data.network.ip})`)} |`);
|
|
383
|
-
lines.push(`| RX | ${mdCell(`${formatBytes(data.network.rxBytes)}
|
|
384
|
-
lines.push(`| TX | ${mdCell(`${formatBytes(data.network.txBytes)}
|
|
408
|
+
lines.push(`| RX | ${mdCell(`${formatBytes(data.network.rxBytes)} · ${formatBytes(data.network.rxRate)}/s`)} |`);
|
|
409
|
+
lines.push(`| TX | ${mdCell(`${formatBytes(data.network.txBytes)} · ${formatBytes(data.network.txRate)}/s`)} |`);
|
|
410
|
+
if (data.network.protocols) {
|
|
411
|
+
lines.push(`| TCP | ${mdCell(`${data.network.protocols.tcp.connections} conns`)} |`);
|
|
412
|
+
lines.push(`| UDP | ${mdCell(`${data.network.protocols.udp.connections} conns`)} |`);
|
|
413
|
+
}
|
|
414
|
+
if (data.network.connectionStates) {
|
|
415
|
+
lines.push(`| Active | ${mdCell(`${data.network.connectionStates.established} ESTABLISHED`)} |`);
|
|
416
|
+
lines.push(`| Listening | ${mdCell(`${data.network.connectionStates.listening}`)} |`);
|
|
417
|
+
}
|
|
418
|
+
if (data.network.listeningPorts && data.network.listeningPorts.length) {
|
|
419
|
+
lines.push(`| Ports | ${mdCell(data.network.listeningPorts.slice(0, 8).join(', '))} |`);
|
|
420
|
+
}
|
|
385
421
|
lines.push('');
|
|
386
422
|
|
|
387
423
|
if (data.packets) {
|
|
@@ -393,6 +429,14 @@ export function formatMarkdown(data: StatsData): string {
|
|
|
393
429
|
lines.push(`| RX | ${mdCell(`${data.packets.rxPackets} pkt`)} |`);
|
|
394
430
|
lines.push(`| TX | ${mdCell(`${data.packets.txPackets} pkt`)} |`);
|
|
395
431
|
lines.push(`| Connections | ${mdCell(`${data.packets.connections} TCP`)} |`);
|
|
432
|
+
if (data.packets.protocolStats) {
|
|
433
|
+
lines.push(`| TCP | ${mdCell(`${data.packets.protocolStats.tcp.connections} conns`)} |`);
|
|
434
|
+
lines.push(`| UDP | ${mdCell(`${data.packets.protocolStats.udp.connections} conns`)} |`);
|
|
435
|
+
}
|
|
436
|
+
if (data.packets.connectionStates) {
|
|
437
|
+
lines.push(`| ESTABLISHED | ${mdCell(`${data.packets.connectionStates.established}`)} |`);
|
|
438
|
+
lines.push(`| LISTENING | ${mdCell(`${data.packets.connectionStates.listening}`)} |`);
|
|
439
|
+
}
|
|
396
440
|
lines.push('');
|
|
397
441
|
}
|
|
398
442
|
|
|
@@ -429,6 +473,17 @@ export function formatMarkdown(data: StatsData): string {
|
|
|
429
473
|
lines.push('');
|
|
430
474
|
}
|
|
431
475
|
|
|
476
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
477
|
+
lines.push('## Blender Renders');
|
|
478
|
+
lines.push('');
|
|
479
|
+
lines.push('| PID | Blend File | Engine | Frames | Completion | Status | Elapsed |');
|
|
480
|
+
lines.push('|---|---|---|---|---|---|---|');
|
|
481
|
+
for (const r of data.blenderRenders) {
|
|
482
|
+
lines.push(`| ${r.pid} | ${mdCell(r.blendFile)} | ${mdCell(r.renderEngine)} | ${r.currentFrame}/${r.totalFrames} | ${r.completionPercent}% | ${r.status} | ${r.elapsedSec}s |`);
|
|
483
|
+
}
|
|
484
|
+
lines.push('');
|
|
485
|
+
}
|
|
486
|
+
|
|
432
487
|
return lines.join('\n');
|
|
433
488
|
}
|
|
434
489
|
|
|
@@ -465,9 +520,16 @@ export function formatCsv(data: StatsData): string {
|
|
|
465
520
|
['thermal', 'cpuTempF', data.thermal.temperatures?.cpu_die != null ? String(celsiusToFahrenheit(data.thermal.temperatures.cpu_die)) : ''],
|
|
466
521
|
['network', 'rxBytes', String(data.network.rxBytes)],
|
|
467
522
|
['network', 'txBytes', String(data.network.txBytes)],
|
|
523
|
+
['network', 'rxRate', String(data.network.rxRate)],
|
|
524
|
+
['network', 'txRate', String(data.network.txRate)],
|
|
468
525
|
['network', 'rxPackets', String(data.network.rxPackets)],
|
|
469
526
|
['network', 'txPackets', String(data.network.txPackets)],
|
|
470
527
|
['network', 'connections', data.network.connections ? String(data.network.connections) : ''],
|
|
528
|
+
['network', 'tcpConns', data.network.protocols ? String(data.network.protocols.tcp.connections) : ''],
|
|
529
|
+
['network', 'udpConns', data.network.protocols ? String(data.network.protocols.udp.connections) : ''],
|
|
530
|
+
['network', 'established', data.network.connectionStates ? String(data.network.connectionStates.established) : ''],
|
|
531
|
+
['network', 'listening', data.network.connectionStates ? String(data.network.connectionStates.listening) : ''],
|
|
532
|
+
['network', 'listeningPorts', data.network.listeningPorts ? data.network.listeningPorts.slice(0, 10).join(';') : ''],
|
|
471
533
|
['battery', 'level', data.battery ? String(data.battery.level) : ''],
|
|
472
534
|
['battery', 'powerSource', data.battery ? data.battery.powerSource : ''],
|
|
473
535
|
['battery', 'condition', data.battery?.condition ?? ''],
|
|
@@ -492,6 +554,16 @@ export function formatCsv(data: StatsData): string {
|
|
|
492
554
|
rows.push(['packets', 'rxPackets', String(data.packets.rxPackets)]);
|
|
493
555
|
rows.push(['packets', 'txPackets', String(data.packets.txPackets)]);
|
|
494
556
|
rows.push(['packets', 'connections', String(data.packets.connections)]);
|
|
557
|
+
if (data.packets.protocolStats) {
|
|
558
|
+
rows.push(['packets', 'tcpConns', String(data.packets.protocolStats.tcp.connections)]);
|
|
559
|
+
rows.push(['packets', 'udpConns', String(data.packets.protocolStats.udp.connections)]);
|
|
560
|
+
}
|
|
561
|
+
if (data.packets.connectionStates) {
|
|
562
|
+
rows.push(['packets', 'established', String(data.packets.connectionStates.established)]);
|
|
563
|
+
rows.push(['packets', 'listening', String(data.packets.connectionStates.listening)]);
|
|
564
|
+
rows.push(['packets', 'timeWait', String(data.packets.connectionStates.timeWait)]);
|
|
565
|
+
rows.push(['packets', 'closeWait', String(data.packets.connectionStates.closeWait)]);
|
|
566
|
+
}
|
|
495
567
|
for (const iface of data.packets.interfaces || []) {
|
|
496
568
|
rows.push(['packets_iface', iface.iface, `${iface.rxPackets},${iface.txPackets},${iface.rxBytes},${iface.txBytes}`]);
|
|
497
569
|
}
|
|
@@ -519,6 +591,21 @@ export function formatCsv(data: StatsData): string {
|
|
|
519
591
|
rows.push(['process', `runtime=${p.pid}`, String(p.runtime)]);
|
|
520
592
|
rows.push(['process', `command=${p.pid}`, p.command]);
|
|
521
593
|
}
|
|
594
|
+
|
|
595
|
+
for (const r of data.blenderRenders || []) {
|
|
596
|
+
rows.push(['blender', `pid=${r.pid}`, `${r.pid}`]);
|
|
597
|
+
rows.push(['blender', `blend_file=${r.pid}`, r.blendFile]);
|
|
598
|
+
rows.push(['blender', `engine=${r.pid}`, r.renderEngine]);
|
|
599
|
+
rows.push(['blender', `frame_start=${r.pid}`, String(r.frameStart)]);
|
|
600
|
+
rows.push(['blender', `frame_end=${r.pid}`, String(r.frameEnd)]);
|
|
601
|
+
rows.push(['blender', `current_frame=${r.pid}`, String(r.currentFrame)]);
|
|
602
|
+
rows.push(['blender', `total_frames=${r.pid}`, String(r.totalFrames)]);
|
|
603
|
+
rows.push(['blender', `completion_pct=${r.pid}`, String(r.completionPercent)]);
|
|
604
|
+
rows.push(['blender', `status=${r.pid}`, r.status]);
|
|
605
|
+
rows.push(['blender', `elapsed_sec=${r.pid}`, String(r.elapsedSec)]);
|
|
606
|
+
rows.push(['blender', `output_path=${r.pid}`, r.outputPath]);
|
|
607
|
+
if (r.sampleCount !== undefined) rows.push(['blender', `samples=${r.pid}`, String(r.sampleCount)]);
|
|
608
|
+
}
|
|
522
609
|
return rows.map(r => r.map(item => `"${String(item).replace(/"/g, '""')}"`).join(',')).join('\n');
|
|
523
610
|
}
|
|
524
611
|
|
package/src/formatters/render.ts
CHANGED
|
@@ -13,7 +13,9 @@ import chalk from 'chalk';
|
|
|
13
13
|
import { sparkline, multiRowSparkline, brailleGraph, multiRowBrailleGraph } from '../sparkline.js';
|
|
14
14
|
import type { History } from '../history.js';
|
|
15
15
|
import type { StatsData, VisibleItems, TableOptions, AnomalyAlert } from './types.js';
|
|
16
|
+
import type { BlenderRenderData } from '../monitors/types.js';
|
|
16
17
|
import { THEMES, type ThemeName, type ThemeColors } from './themes.js';
|
|
18
|
+
import { explainAnomaliesLocally } from '../ai/models.js';
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
// --- low-level box drawing ---------------------------------------------------
|
|
@@ -313,8 +315,17 @@ function batteryCard(data: StatsData, contentWidth: number): string[] | null {
|
|
|
313
315
|
if (data.battery.estimatedTimeToEmpty && data.battery.state !== 'charged') {
|
|
314
316
|
lines.push(statRow('Est. Empty', data.battery.estimatedTimeToEmpty));
|
|
315
317
|
}
|
|
316
|
-
if (data.battery.
|
|
317
|
-
lines.push(statRow('
|
|
318
|
+
if (data.battery.powerWatts !== undefined && data.battery.powerWatts > 0 && data.battery.state !== 'charged') {
|
|
319
|
+
lines.push(statRow('Power Draw', `${data.battery.powerWatts.toFixed(2)} W`));
|
|
320
|
+
}
|
|
321
|
+
if (data.battery.smartPrediction) {
|
|
322
|
+
const sp = data.battery.smartPrediction;
|
|
323
|
+
lines.push('');
|
|
324
|
+
lines.push(chalk.bold('SMART RUNTIME FORECAST:'));
|
|
325
|
+
lines.push(statRow('Light Load', `${chalk.green(sp.scenarios.idle.timeRemaining)} (~${sp.scenarios.idle.ratePerHour}%/h)`));
|
|
326
|
+
lines.push(statRow('Current Load', `${chalk.yellow(sp.scenarios.current.timeRemaining)} (~${sp.scenarios.current.ratePerHour}%/h)`));
|
|
327
|
+
lines.push(statRow('Heavy Load', `${chalk.red(sp.scenarios.heavy.timeRemaining)} (~${sp.scenarios.heavy.ratePerHour}%/h)`));
|
|
328
|
+
lines.push(chalk.dim(`Advice: ${sp.recommendation}`));
|
|
318
329
|
}
|
|
319
330
|
return lines;
|
|
320
331
|
}
|
|
@@ -333,11 +344,102 @@ function thermalCard(data: StatsData, contentWidth: number): string[] {
|
|
|
333
344
|
|
|
334
345
|
function networkCard(data: StatsData, contentWidth: number): string[] {
|
|
335
346
|
const valW = Math.max(6, contentWidth - 8);
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
347
|
+
const lines: string[] = [];
|
|
348
|
+
lines.push(statRow('Iface', truncatePlain(`${data.network.interface} (${data.network.ip})`, valW)));
|
|
349
|
+
const rxTotal = formatBytes(data.network.rxBytes);
|
|
350
|
+
const txTotal = formatBytes(data.network.txBytes);
|
|
351
|
+
const rxRateStr = formatBytes(data.network.rxRate);
|
|
352
|
+
const txRateStr = formatBytes(data.network.txRate);
|
|
353
|
+
lines.push(statRow('RX', `${rxTotal} · ${rxRateStr}/s`));
|
|
354
|
+
lines.push(statRow('TX', `${txTotal} · ${txRateStr}/s`));
|
|
355
|
+
|
|
356
|
+
const totalBytes = data.network.rxBytes + data.network.txBytes;
|
|
357
|
+
if (totalBytes > 0) {
|
|
358
|
+
const rxPct = Math.round((data.network.rxBytes / totalBytes) * 100);
|
|
359
|
+
const barW = Math.max(4, contentWidth - 20);
|
|
360
|
+
const rxBar = gaugeBar(rxPct, barW);
|
|
361
|
+
lines.push(statRow('RX/TX', `${rxBar} ${rxPct}%`));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (data.network.protocols) {
|
|
365
|
+
const p = data.network.protocols;
|
|
366
|
+
const totalConns = (p.tcp.connections || 0) + (p.udp.connections || 0);
|
|
367
|
+
if (totalConns > 0) {
|
|
368
|
+
const tcpPct = Math.round((p.tcp.connections / totalConns) * 100);
|
|
369
|
+
lines.push(statRow('TCP', `${p.tcp.connections} conns · ${gaugeBar(tcpPct, Math.max(4, contentWidth - 20))} ${tcpPct}%`));
|
|
370
|
+
lines.push(statRow('UDP', `${p.udp.connections} conns`));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (data.network.connectionStates) {
|
|
374
|
+
const cs = data.network.connectionStates;
|
|
375
|
+
const total = cs.established + cs.listening + cs.timeWait + cs.closeWait + cs.other;
|
|
376
|
+
if (total > 0) {
|
|
377
|
+
const estPct = Math.round((cs.established / total) * 100);
|
|
378
|
+
lines.push(statRow('Active', `${cs.established} ESTABLISHED · ${estPct}%`));
|
|
379
|
+
lines.push(statRow('Listen', `${cs.listening} LISTEN`));
|
|
380
|
+
if (cs.timeWait > 0) lines.push(statRow('TimeWait', `${cs.timeWait}`));
|
|
381
|
+
if (cs.closeWait > 0) lines.push(statRow('CloseWait', `${cs.closeWait}`));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (data.network.listeningPorts && data.network.listeningPorts.length > 0) {
|
|
385
|
+
const ports = data.network.listeningPorts.slice(0, 8).join(', ');
|
|
386
|
+
lines.push(statRow('Ports', truncatePlain(ports, valW)));
|
|
387
|
+
}
|
|
388
|
+
if (data.network.topRemoteHosts && data.network.topRemoteHosts.length > 0) {
|
|
389
|
+
for (const host of data.network.topRemoteHosts.slice(0, 3)) {
|
|
390
|
+
lines.push(statRow('Host', truncatePlain(`${host.host} (${host.connectionCount})`, valW)));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return lines;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function networkDetailCard(data: StatsData, width: number, theme: ThemeColors): string[] {
|
|
397
|
+
const contentWidth = width - 4;
|
|
398
|
+
const lines = networkCard(data, contentWidth);
|
|
399
|
+
|
|
400
|
+
if (data.packets && data.packets.allProcesses && data.packets.allProcesses.length > 0) {
|
|
401
|
+
lines.push('');
|
|
402
|
+
lines.push(chalk.bold('PROCESSES & BANDWIDTH USAGE:'));
|
|
403
|
+
lines.push(chalk.dim('─'.repeat(contentWidth)));
|
|
404
|
+
|
|
405
|
+
const pWidth = 8;
|
|
406
|
+
const rxWidth = 12;
|
|
407
|
+
const txWidth = 12;
|
|
408
|
+
const totalWidth = 12;
|
|
409
|
+
const cmdWidth = Math.max(15, contentWidth - (pWidth + rxWidth + txWidth + totalWidth + 4 * 2));
|
|
410
|
+
|
|
411
|
+
lines.push(
|
|
412
|
+
chalk.bold('PID'.padEnd(pWidth)) + ' ' +
|
|
413
|
+
chalk.bold('COMMAND'.padEnd(cmdWidth)) + ' ' +
|
|
414
|
+
chalk.bold('RX'.padStart(rxWidth)) + ' ' +
|
|
415
|
+
chalk.bold('TX'.padStart(txWidth)) + ' ' +
|
|
416
|
+
chalk.bold('TOTAL'.padStart(totalWidth))
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
const sorted = [...data.packets.allProcesses]
|
|
420
|
+
.sort((a, b) => (b.rxBytes + b.txBytes) - (a.rxBytes + a.txBytes));
|
|
421
|
+
|
|
422
|
+
for (const proc of sorted) {
|
|
423
|
+
if (proc.rxBytes + proc.txBytes === 0) continue;
|
|
424
|
+
const rxStr = formatBytes(proc.rxBytes).padStart(rxWidth);
|
|
425
|
+
const txStr = formatBytes(proc.txBytes).padStart(txWidth);
|
|
426
|
+
const totalStr = formatBytes(proc.rxBytes + proc.txBytes).padStart(totalWidth);
|
|
427
|
+
const cmdStr = truncatePlain(proc.command, cmdWidth).padEnd(cmdWidth);
|
|
428
|
+
|
|
429
|
+
lines.push(
|
|
430
|
+
chalk.cyan(proc.pid.toString().padEnd(pWidth)) + ' ' +
|
|
431
|
+
cmdStr + ' ' +
|
|
432
|
+
chalk.yellow(rxStr) + ' ' +
|
|
433
|
+
chalk.yellow(txStr) + ' ' +
|
|
434
|
+
chalk.bold(totalStr)
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
lines.push('');
|
|
439
|
+
lines.push(chalk.dim('No active network process data available.'));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return lines;
|
|
341
443
|
}
|
|
342
444
|
|
|
343
445
|
function packetCard(data: StatsData, contentWidth: number): string[] | null {
|
|
@@ -347,8 +449,19 @@ function packetCard(data: StatsData, contentWidth: number): string[] | null {
|
|
|
347
449
|
lines.push(statRow('RX', `${data.packets.rxPackets} pkt`));
|
|
348
450
|
lines.push(statRow('TX', `${data.packets.txPackets} pkt`));
|
|
349
451
|
lines.push(statRow('Conns', `${data.packets.connections} TCP`));
|
|
452
|
+
if (data.packets.protocolStats) {
|
|
453
|
+
const ps = data.packets.protocolStats;
|
|
454
|
+
lines.push(statRow('TCP', `${ps.tcp.connections} conns`));
|
|
455
|
+
lines.push(statRow('UDP', `${ps.udp.connections} conns`));
|
|
456
|
+
}
|
|
457
|
+
if (data.packets.connectionStates) {
|
|
458
|
+
const cs = data.packets.connectionStates;
|
|
459
|
+
lines.push(statRow('ESTAB', `${cs.established}`));
|
|
460
|
+
lines.push(statRow('LISTEN', `${cs.listening}`));
|
|
461
|
+
}
|
|
350
462
|
if (data.packets.topProcesses && data.packets.topProcesses.length) {
|
|
351
|
-
|
|
463
|
+
const topProc = data.packets.topProcesses[0];
|
|
464
|
+
lines.push(statRow('Top Proc', `${truncatePlain(topProc.command, contentWidth - 25)} (${formatBytes(topProc.rxBytes + topProc.txBytes)})`));
|
|
352
465
|
}
|
|
353
466
|
if (data.packets.allProcesses && data.packets.allProcesses.length > 1) {
|
|
354
467
|
lines.push(statRow('Net Procs', `${data.packets.allProcesses.length} processes`));
|
|
@@ -392,13 +505,14 @@ export const TAB_DEFS: { id: string; label: string; key: string }[] = [
|
|
|
392
505
|
{ id: 'power', label: 'Power', key: '4' },
|
|
393
506
|
{ id: 'battery', label: 'Battery', key: '5' },
|
|
394
507
|
{ id: 'thermal', label: 'Thermal', key: '6' },
|
|
395
|
-
{ id: '
|
|
396
|
-
{ id: '
|
|
397
|
-
{ id: '
|
|
398
|
-
{ id: '
|
|
399
|
-
{ id: 'process', label: 'Process', key: 'P' },
|
|
508
|
+
{ id: 'packets', label: 'Conns', key: '7' },
|
|
509
|
+
{ id: 'tasks', label: 'Tasks', key: '8' },
|
|
510
|
+
{ id: 'disk', label: 'Disk', key: '9' },
|
|
511
|
+
{ id: 'process', label: 'Process', key: '0' },
|
|
400
512
|
{ id: 'p2p', label: 'P2P', key: 'R' },
|
|
401
513
|
{ id: 'anomalies', label: 'Anomalies', key: 'A' },
|
|
514
|
+
{ id: 'containers', label: 'Containers', key: 'C' },
|
|
515
|
+
{ id: 'blender', label: 'Blender', key: 'B' },
|
|
402
516
|
];
|
|
403
517
|
|
|
404
518
|
/** The number of leading columns of indent before the tab bar's first character (see `tabBar`). */
|
|
@@ -620,6 +734,58 @@ function diskTableLines(
|
|
|
620
734
|
return [head, borderAccent('─'.repeat(contentWidth)), ...rows];
|
|
621
735
|
}
|
|
622
736
|
|
|
737
|
+
function containersTableLines(
|
|
738
|
+
containers: { id: string; name: string; cpuPercent: number; memUsage: string; memPercent: number; netIO: string; blockIO: string; pids: number }[],
|
|
739
|
+
contentWidth: number,
|
|
740
|
+
borderAccent = THEMES.default.border
|
|
741
|
+
): string[] {
|
|
742
|
+
const idW = 14, cpuW = 8, memW = 8, memUsgW = 20, netW = 20, blockW = 20, pidsW = 6;
|
|
743
|
+
const nameW = Math.max(10, contentWidth - idW - cpuW - memW - memUsgW - netW - blockW - pidsW - 7);
|
|
744
|
+
|
|
745
|
+
const head =
|
|
746
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' CONTAINER ID ') +
|
|
747
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' CPU% ') +
|
|
748
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' MEM% ') +
|
|
749
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' MEM USAGE ') +
|
|
750
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' NET I/O ') +
|
|
751
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' BLOCK I/O ') +
|
|
752
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' PIDS ') +
|
|
753
|
+
chalk.bold.hex('#f8f8f2').bgHex('#44475a')(' NAME'.padEnd(nameW + 1));
|
|
754
|
+
|
|
755
|
+
const rows = containers.map(c => {
|
|
756
|
+
const cpuStr = pctColor(c.cpuPercent)(`${c.cpuPercent.toFixed(1)}`.padEnd(cpuW));
|
|
757
|
+
const memStr = pctColor(c.memPercent)(`${c.memPercent.toFixed(1)}`.padEnd(memW));
|
|
758
|
+
|
|
759
|
+
return (
|
|
760
|
+
truncatePlain(c.id, idW - 1).padEnd(idW) +
|
|
761
|
+
cpuStr +
|
|
762
|
+
memStr +
|
|
763
|
+
c.memUsage.padEnd(memUsgW) +
|
|
764
|
+
c.netIO.padEnd(netW) +
|
|
765
|
+
c.blockIO.padEnd(blockW) +
|
|
766
|
+
String(c.pids).padEnd(pidsW) +
|
|
767
|
+
truncatePlain(c.name, nameW)
|
|
768
|
+
);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
return [head, borderAccent('─'.repeat(contentWidth)), ...rows];
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function blenderCard(renders: BlenderRenderData[], contentWidth: number, theme: ThemeColors): string[] {
|
|
775
|
+
if (!renders.length) return ['No active Blender renders detected'];
|
|
776
|
+
const lines: string[] = [];
|
|
777
|
+
for (const r of renders.slice(0, 8)) {
|
|
778
|
+
const statusColor = r.status === 'rendering' ? chalk.green : r.status === 'finalizing' ? chalk.yellow : r.status === 'loading' ? chalk.cyan : chalk.dim;
|
|
779
|
+
const pctStr = `${r.completionPercent}%`;
|
|
780
|
+
const barW = Math.max(4, contentWidth - 28);
|
|
781
|
+
const bar = gaugeBar(r.completionPercent, barW);
|
|
782
|
+
lines.push(`${theme.blender(`PID ${r.pid}`)} ${truncatePlain(r.blendFile, 18)} ${r.renderEngine}`);
|
|
783
|
+
lines.push(` Fra ${String(r.currentFrame).padStart(4)}/${r.totalFrames} ${bar} ${pctStr}`);
|
|
784
|
+
lines.push(` ${r.elapsedSec}s elapsed · ${statusColor(r.status)}${r.sampleCount !== undefined ? ` · ${r.sampleCount} spp` : ''}`);
|
|
785
|
+
}
|
|
786
|
+
return lines;
|
|
787
|
+
}
|
|
788
|
+
|
|
623
789
|
// --- main dashboard ------------------------------------------------------------
|
|
624
790
|
|
|
625
791
|
function detailPanelTitle(panel: string, trackedPid?: number | null): string {
|
|
@@ -635,6 +801,8 @@ function detailPanelTitle(panel: string, trackedPid?: number | null): string {
|
|
|
635
801
|
tasks: 'Tasks',
|
|
636
802
|
disk: 'Disk',
|
|
637
803
|
process: trackedPid !== undefined && trackedPid !== null ? `Processes (Following PID ${trackedPid})` : 'Processes',
|
|
804
|
+
containers: 'Containers',
|
|
805
|
+
blender: 'Blender Renders',
|
|
638
806
|
};
|
|
639
807
|
return map[panel] || panel;
|
|
640
808
|
}
|
|
@@ -654,39 +822,81 @@ function activeDetailLines(data: StatsData, activePanel: string, width: number,
|
|
|
654
822
|
}
|
|
655
823
|
case 'power': {
|
|
656
824
|
const power = powerCard(data);
|
|
657
|
-
if (!power
|
|
658
|
-
|
|
659
|
-
if (power) lines.push(...power);
|
|
660
|
-
if (data.battery?.powerWatts !== undefined && data.battery.powerWatts > 0) {
|
|
661
|
-
lines.push(statRow('Battery', `${data.battery.powerWatts.toFixed(2)} W`));
|
|
662
|
-
}
|
|
663
|
-
return lines.length ? lines : ['No power data available'];
|
|
825
|
+
if (!power) return ['No power data available'];
|
|
826
|
+
return power;
|
|
664
827
|
}
|
|
665
828
|
case 'battery':
|
|
666
829
|
return data.battery ? batteryCard(data, contentWidth) : ['No battery data available'];
|
|
667
830
|
case 'thermal':
|
|
668
831
|
return thermalCard(data, contentWidth);
|
|
669
832
|
case 'network':
|
|
670
|
-
return
|
|
833
|
+
return networkDetailCard(data, width, theme);
|
|
671
834
|
case 'packets': {
|
|
672
|
-
if (!data.packets
|
|
673
|
-
|
|
674
|
-
lines.push(statRow('Total', `${data.packets.totalPackets} pkt`));
|
|
675
|
-
lines.push(statRow('RX', `${data.packets.rxPackets} pkt`));
|
|
676
|
-
lines.push(statRow('TX', `${data.packets.txPackets} pkt`));
|
|
677
|
-
lines.push(statRow('Conns', `${data.packets.connections} TCP`));
|
|
678
|
-
if (data.packets.interfaces && data.packets.interfaces.length) {
|
|
679
|
-
lines.push(statRow('Ifaces', `${data.packets.interfaces.length} active`));
|
|
680
|
-
for (const iface of data.packets.interfaces.slice(0, 8)) {
|
|
681
|
-
lines.push(statRow(` ${iface.iface}`, `RX ${formatBytes(iface.rxBytes)} / TX ${formatBytes(iface.txBytes)}`));
|
|
682
|
-
}
|
|
835
|
+
if (!data.packets || !data.packets.allProcesses || !data.packets.allProcesses.length) {
|
|
836
|
+
return ['No advanced network tracking data available.'];
|
|
683
837
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
838
|
+
|
|
839
|
+
const lines: string[] = [];
|
|
840
|
+
const pWidth = 8;
|
|
841
|
+
const cmdWidth = 15;
|
|
842
|
+
const protoWidth = 5;
|
|
843
|
+
const stateWidth = 11;
|
|
844
|
+
const rxWidth = 10;
|
|
845
|
+
const txWidth = 10;
|
|
846
|
+
|
|
847
|
+
const remaining = Math.max(20, width - (pWidth + cmdWidth + protoWidth + stateWidth + rxWidth + txWidth + 7 * 2));
|
|
848
|
+
const localWidth = Math.floor(remaining * 0.4);
|
|
849
|
+
const remoteWidth = remaining - localWidth;
|
|
850
|
+
|
|
851
|
+
const header =
|
|
852
|
+
chalk.bold('PID'.padEnd(pWidth)) + ' ' +
|
|
853
|
+
chalk.bold('COMMAND'.padEnd(cmdWidth)) + ' ' +
|
|
854
|
+
chalk.bold('PROTO'.padEnd(protoWidth)) + ' ' +
|
|
855
|
+
chalk.bold('LOCAL'.padEnd(localWidth)) + ' ' +
|
|
856
|
+
chalk.bold('REMOTE'.padEnd(remoteWidth)) + ' ' +
|
|
857
|
+
chalk.bold('STATE'.padEnd(stateWidth)) + ' ' +
|
|
858
|
+
chalk.bold('RX'.padStart(rxWidth)) + ' ' +
|
|
859
|
+
chalk.bold('TX'.padStart(txWidth));
|
|
860
|
+
|
|
861
|
+
lines.push(header);
|
|
862
|
+
lines.push(chalk.dim('─'.repeat(width)));
|
|
863
|
+
|
|
864
|
+
for (const proc of data.packets.allProcesses) {
|
|
865
|
+
if (!proc.connections || proc.connections.length === 0) continue;
|
|
866
|
+
|
|
867
|
+
const summaryCmd = truncatePlain(proc.command, cmdWidth + protoWidth + localWidth + remoteWidth + stateWidth + 8);
|
|
868
|
+
const summaryRx = formatBytes(proc.rxBytes).padStart(rxWidth);
|
|
869
|
+
const summaryTx = formatBytes(proc.txBytes).padStart(txWidth);
|
|
870
|
+
|
|
871
|
+
lines.push(
|
|
872
|
+
chalk.cyan(proc.pid.toString().padEnd(pWidth)) + ' ' +
|
|
873
|
+
chalk.cyan(summaryCmd.padEnd(width - pWidth - rxWidth - txWidth - 6)) + ' ' +
|
|
874
|
+
chalk.yellow(summaryRx) + ' ' +
|
|
875
|
+
chalk.yellow(summaryTx)
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
for (const conn of proc.connections) {
|
|
879
|
+
const cProto = chalk.dim(truncatePlain(conn.protocol, protoWidth).padEnd(protoWidth));
|
|
880
|
+
const cLocal = chalk.dim(truncatePlain(conn.local, localWidth).padEnd(localWidth));
|
|
881
|
+
const cRemote = truncatePlain(conn.remote, remoteWidth).padEnd(remoteWidth);
|
|
882
|
+
|
|
883
|
+
const cStateStr = conn.state === 'Established' ? chalk.green('ESTABLISHED'.padEnd(stateWidth)) : chalk.dim(truncatePlain(conn.state, stateWidth).padEnd(stateWidth));
|
|
884
|
+
const cRx = chalk.dim(formatBytes(conn.rxBytes).padStart(rxWidth));
|
|
885
|
+
const cTx = chalk.dim(formatBytes(conn.txBytes).padStart(txWidth));
|
|
886
|
+
|
|
887
|
+
lines.push(
|
|
888
|
+
' '.repeat(pWidth) + ' ' +
|
|
889
|
+
' '.repeat(cmdWidth) + ' ' +
|
|
890
|
+
cProto + ' ' +
|
|
891
|
+
cLocal + ' ' +
|
|
892
|
+
cRemote + ' ' +
|
|
893
|
+
cStateStr + ' ' +
|
|
894
|
+
cRx + ' ' +
|
|
895
|
+
cTx
|
|
896
|
+
);
|
|
688
897
|
}
|
|
689
898
|
}
|
|
899
|
+
|
|
690
900
|
return lines;
|
|
691
901
|
}
|
|
692
902
|
case 'tasks':
|
|
@@ -749,15 +959,41 @@ function activeDetailLines(data: StatsData, activePanel: string, width: number,
|
|
|
749
959
|
const lines: string[] = [];
|
|
750
960
|
// Sort by timestamp descending
|
|
751
961
|
const sorted = [...opts.anomalyHistory].sort((a, b) => (b.timestamp?.getTime() || 0) - (a.timestamp?.getTime() || 0));
|
|
752
|
-
for (const a of sorted) {
|
|
962
|
+
for (const a of sorted.slice(0, 10)) {
|
|
753
963
|
const color = a.severity === 'critical' ? chalk.red.bold : chalk.yellow;
|
|
754
964
|
const direction = a.zScore > 0 ? '↑ spike' : '↓ drop';
|
|
755
965
|
const valStr = a.metric.toLowerCase().includes('net') ? `${formatBytes(a.value)}/s` : `${a.value.toFixed(1)}%`;
|
|
756
966
|
const timeStr = a.timestamp ? a.timestamp.toLocaleTimeString() : '';
|
|
757
967
|
lines.push(color(` ${timeStr.padEnd(10)} ⚠ ${a.metric.padEnd(8)}: ${valStr.padEnd(12)} (${direction}, σ=${a.zScore.toFixed(1)})`));
|
|
758
968
|
}
|
|
969
|
+
lines.push('');
|
|
970
|
+
// In-TUI AI Model Diagnosis section (offline, zero external server needed)
|
|
971
|
+
const modelId = opts.aiModel || 'expert-rules-v1';
|
|
972
|
+
const aiAnalysis = explainAnomaliesLocally(sorted, modelId);
|
|
973
|
+
for (const al of aiAnalysis.split('\n')) {
|
|
974
|
+
if (al.startsWith('Diagnosis Engine:')) {
|
|
975
|
+
lines.push(chalk.bold.magenta(al));
|
|
976
|
+
} else if (al.startsWith('Tip:')) {
|
|
977
|
+
lines.push(chalk.cyan.bold(al));
|
|
978
|
+
} else if (al.startsWith(' • Recommendation:')) {
|
|
979
|
+
lines.push(chalk.green(al));
|
|
980
|
+
} else if (al.startsWith(' • Cause:')) {
|
|
981
|
+
lines.push(chalk.yellow(al));
|
|
982
|
+
} else if (al.startsWith(' • Verdict:')) {
|
|
983
|
+
lines.push(chalk.dim(al));
|
|
984
|
+
} else {
|
|
985
|
+
lines.push(chalk.dim(al));
|
|
986
|
+
}
|
|
987
|
+
}
|
|
759
988
|
return lines;
|
|
760
989
|
}
|
|
990
|
+
case 'containers': {
|
|
991
|
+
return data.containers && data.containers.length ? containersTableLines(data.containers, width - 4, theme.border) : ['No container data available'];
|
|
992
|
+
}
|
|
993
|
+
case 'blender': {
|
|
994
|
+
const renders = data.blenderRenders && data.blenderRenders.length ? data.blenderRenders : [];
|
|
995
|
+
return blenderCard(renders, contentWidth, theme);
|
|
996
|
+
}
|
|
761
997
|
default:
|
|
762
998
|
return null;
|
|
763
999
|
}
|
|
@@ -1034,7 +1270,7 @@ export function formatTable(data: StatsData, opts: TableOptions = {}): string {
|
|
|
1034
1270
|
lines.push(gaugeRow('GPU', data.gpu.utilization, contentWidth));
|
|
1035
1271
|
}
|
|
1036
1272
|
const tempC = data.cpu.temperature ?? data.thermal.temperatures?.cpu_die;
|
|
1037
|
-
if (tempC
|
|
1273
|
+
if (tempC != null) {
|
|
1038
1274
|
const tempStr = formatTempUnit(tempC, opts.tempUnit);
|
|
1039
1275
|
lines.push(statRow('Temp', thermalColor(data.thermal.pressureLevel)(tempStr)));
|
|
1040
1276
|
}
|
package/src/formatters/themes.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface ThemeColors {
|
|
|
26
26
|
disk: (s: string) => string;
|
|
27
27
|
graphs: (s: string) => string;
|
|
28
28
|
process: (s: string) => string;
|
|
29
|
+
blender: (s: string) => string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
@@ -41,6 +42,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
41
42
|
disk: chalk.hex('#f472b6'),
|
|
42
43
|
graphs: chalk.hex('#22d3ee'),
|
|
43
44
|
process: chalk.hex('#e5e7eb'),
|
|
45
|
+
blender: chalk.hex('#f97316'),
|
|
44
46
|
},
|
|
45
47
|
dracula: {
|
|
46
48
|
border: chalk.hex('#6272a4'),
|
|
@@ -54,6 +56,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
54
56
|
disk: chalk.hex('#ff5555'),
|
|
55
57
|
graphs: chalk.hex('#8be9fd'),
|
|
56
58
|
process: chalk.hex('#f8f8f2'),
|
|
59
|
+
blender: chalk.hex('#ffb86c'),
|
|
57
60
|
},
|
|
58
61
|
cyberpunk: {
|
|
59
62
|
border: chalk.hex('#ff0055'),
|
|
@@ -67,6 +70,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
67
70
|
disk: chalk.hex('#ff007f'),
|
|
68
71
|
graphs: chalk.hex('#00ffcc'),
|
|
69
72
|
process: chalk.hex('#ffffff'),
|
|
73
|
+
blender: chalk.hex('#ff6600'),
|
|
70
74
|
},
|
|
71
75
|
monochrome: {
|
|
72
76
|
border: chalk.gray,
|
|
@@ -80,6 +84,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
80
84
|
disk: chalk.white.bold,
|
|
81
85
|
graphs: chalk.white.bold,
|
|
82
86
|
process: chalk.white.bold,
|
|
87
|
+
blender: chalk.white.bold,
|
|
83
88
|
},
|
|
84
89
|
nord: {
|
|
85
90
|
border: chalk.hex('#4c566a'),
|
|
@@ -93,6 +98,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
93
98
|
disk: chalk.hex('#b48ead'),
|
|
94
99
|
graphs: chalk.hex('#88c0d0'),
|
|
95
100
|
process: chalk.hex('#eceff4'),
|
|
101
|
+
blender: chalk.hex('#d08770'),
|
|
96
102
|
},
|
|
97
103
|
gruvbox: {
|
|
98
104
|
border: chalk.hex('#928374'),
|
|
@@ -106,6 +112,7 @@ export const BUILTIN_THEMES: Record<string, ThemeColors> = {
|
|
|
106
112
|
disk: chalk.hex('#d3869b'),
|
|
107
113
|
graphs: chalk.hex('#fb4934'),
|
|
108
114
|
process: chalk.hex('#ebdbb2'),
|
|
115
|
+
blender: chalk.hex('#fe8019'),
|
|
109
116
|
},
|
|
110
117
|
};
|
|
111
118
|
|
|
@@ -134,6 +141,7 @@ export function loadCustomThemes(): Record<string, ThemeColors> {
|
|
|
134
141
|
disk: buildColor(data.disk),
|
|
135
142
|
graphs: buildColor(data.graphs),
|
|
136
143
|
process: buildColor(data.process),
|
|
144
|
+
blender: buildColor(data.blender),
|
|
137
145
|
};
|
|
138
146
|
} catch {
|
|
139
147
|
// ignore bad theme file
|