postboy-tui 1.3.9 → 1.4.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/dist/cli.js +645 -126
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -67534,7 +67534,7 @@ var useFocusManager = () => {
|
|
|
67534
67534
|
};
|
|
67535
67535
|
var use_focus_manager_default = useFocusManager;
|
|
67536
67536
|
// src/ui/app/ui.tsx
|
|
67537
|
-
var
|
|
67537
|
+
var import_react35 = __toESM(require_react(), 1);
|
|
67538
67538
|
|
|
67539
67539
|
// src/utils/history.ts
|
|
67540
67540
|
import { promises as fs2 } from "fs";
|
|
@@ -67626,7 +67626,7 @@ var historyManager = new HistoryManager;
|
|
|
67626
67626
|
import http from "http";
|
|
67627
67627
|
import https from "https";
|
|
67628
67628
|
import { URL as URL2 } from "url";
|
|
67629
|
-
function sendRequest({ method, url, headers = {}, body }) {
|
|
67629
|
+
function sendRequest({ method, url, headers = {}, body, onProgress }) {
|
|
67630
67630
|
return new Promise((resolve, reject) => {
|
|
67631
67631
|
const urlObj = new URL2(url);
|
|
67632
67632
|
const isHttps = urlObj.protocol === "https:";
|
|
@@ -67649,12 +67649,26 @@ function sendRequest({ method, url, headers = {}, body }) {
|
|
|
67649
67649
|
const req = reqModule.request(options, (res) => {
|
|
67650
67650
|
timings.ttfb = performance.now();
|
|
67651
67651
|
let data = "";
|
|
67652
|
+
let bytesReceived = 0;
|
|
67653
|
+
const totalBytes = parseInt(res.headers["content-length"] || "0", 10);
|
|
67654
|
+
const downloadStartTime = performance.now();
|
|
67652
67655
|
res.on("data", (chunk) => {
|
|
67653
67656
|
data += chunk;
|
|
67657
|
+
bytesReceived += Buffer.byteLength(chunk);
|
|
67658
|
+
if (onProgress) {
|
|
67659
|
+
const elapsed = (performance.now() - downloadStartTime) / 1000;
|
|
67660
|
+
const speed = elapsed > 0 ? bytesReceived / elapsed : 0;
|
|
67661
|
+
onProgress({
|
|
67662
|
+
bytesReceived,
|
|
67663
|
+
totalBytes,
|
|
67664
|
+
speed,
|
|
67665
|
+
elapsed
|
|
67666
|
+
});
|
|
67667
|
+
}
|
|
67654
67668
|
});
|
|
67655
67669
|
res.on("end", () => {
|
|
67656
67670
|
timings.end = performance.now();
|
|
67657
|
-
const contentLength =
|
|
67671
|
+
const contentLength = totalBytes || Buffer.byteLength(data, "utf8");
|
|
67658
67672
|
const metrics = {
|
|
67659
67673
|
dnsLookup: timings.dnsLookup - timings.start,
|
|
67660
67674
|
tcpConnection: timings.tcpConnection - timings.dnsLookup,
|
|
@@ -69368,6 +69382,140 @@ var JsonSyntaxHighlight = import_react31.default.memo(({ jsonString, theme }) =>
|
|
|
69368
69382
|
// src/ui/app/components/responsepanel.tsx
|
|
69369
69383
|
var import_react32 = __toESM(require_react(), 1);
|
|
69370
69384
|
var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
|
|
69385
|
+
var StatusBadge = ({ status, statusText, theme }) => {
|
|
69386
|
+
const [glowFrame, setGlowFrame] = import_react32.useState(0);
|
|
69387
|
+
const statusNum = parseInt(status, 10);
|
|
69388
|
+
import_react32.useEffect(() => {
|
|
69389
|
+
const interval = setInterval(() => {
|
|
69390
|
+
setGlowFrame((prev) => (prev + 1) % 4);
|
|
69391
|
+
}, 300);
|
|
69392
|
+
return () => clearInterval(interval);
|
|
69393
|
+
}, []);
|
|
69394
|
+
const getStatusIcon = () => {
|
|
69395
|
+
if (statusNum >= 200 && statusNum < 300)
|
|
69396
|
+
return ["✓", "✔", "☑", "✓"][glowFrame];
|
|
69397
|
+
if (statusNum >= 300 && statusNum < 400)
|
|
69398
|
+
return ["↪", "→", "⇒", "↪"][glowFrame];
|
|
69399
|
+
if (statusNum >= 400 && statusNum < 500)
|
|
69400
|
+
return ["⚠", "⚡", "⚠", "⚡"][glowFrame];
|
|
69401
|
+
if (statusNum >= 500)
|
|
69402
|
+
return ["✗", "✘", "☒", "✗"][glowFrame];
|
|
69403
|
+
return "●";
|
|
69404
|
+
};
|
|
69405
|
+
const getBadgeStyle = () => {
|
|
69406
|
+
if (statusNum >= 200 && statusNum < 300)
|
|
69407
|
+
return { bg: theme.colors.success, icon: getStatusIcon(), label: "SUCCESS" };
|
|
69408
|
+
if (statusNum >= 300 && statusNum < 400)
|
|
69409
|
+
return { bg: theme.colors.accent, icon: getStatusIcon(), label: "REDIRECT" };
|
|
69410
|
+
if (statusNum >= 400 && statusNum < 500)
|
|
69411
|
+
return { bg: theme.colors.error, icon: getStatusIcon(), label: "CLIENT ERR" };
|
|
69412
|
+
if (statusNum >= 500)
|
|
69413
|
+
return { bg: theme.colors.error, icon: getStatusIcon(), label: "SERVER ERR" };
|
|
69414
|
+
return { bg: theme.colors.muted, icon: "●", label: "UNKNOWN" };
|
|
69415
|
+
};
|
|
69416
|
+
const style = getBadgeStyle();
|
|
69417
|
+
const glowChars = ["░", "▒", "▓", "▒"];
|
|
69418
|
+
if (!status || status === "Error") {
|
|
69419
|
+
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
69420
|
+
children: [
|
|
69421
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69422
|
+
color: theme.colors.error,
|
|
69423
|
+
bold: true,
|
|
69424
|
+
children: "✗ "
|
|
69425
|
+
}, undefined, false, undefined, this),
|
|
69426
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69427
|
+
color: theme.colors.error,
|
|
69428
|
+
children: statusText || "Error"
|
|
69429
|
+
}, undefined, false, undefined, this)
|
|
69430
|
+
]
|
|
69431
|
+
}, undefined, true, undefined, this);
|
|
69432
|
+
}
|
|
69433
|
+
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
69434
|
+
children: [
|
|
69435
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69436
|
+
color: style.bg,
|
|
69437
|
+
dimColor: true,
|
|
69438
|
+
children: glowChars[glowFrame]
|
|
69439
|
+
}, undefined, false, undefined, this),
|
|
69440
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69441
|
+
backgroundColor: style.bg,
|
|
69442
|
+
color: theme.colors.white,
|
|
69443
|
+
bold: true,
|
|
69444
|
+
children: [
|
|
69445
|
+
" ",
|
|
69446
|
+
style.icon,
|
|
69447
|
+
" ",
|
|
69448
|
+
status,
|
|
69449
|
+
" "
|
|
69450
|
+
]
|
|
69451
|
+
}, undefined, true, undefined, this),
|
|
69452
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69453
|
+
color: style.bg,
|
|
69454
|
+
dimColor: true,
|
|
69455
|
+
children: glowChars[glowFrame]
|
|
69456
|
+
}, undefined, false, undefined, this),
|
|
69457
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69458
|
+
color: theme.colors.muted,
|
|
69459
|
+
children: " │ "
|
|
69460
|
+
}, undefined, false, undefined, this),
|
|
69461
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69462
|
+
color: style.bg,
|
|
69463
|
+
bold: true,
|
|
69464
|
+
children: style.label
|
|
69465
|
+
}, undefined, false, undefined, this),
|
|
69466
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69467
|
+
color: theme.colors.muted,
|
|
69468
|
+
children: " │ "
|
|
69469
|
+
}, undefined, false, undefined, this),
|
|
69470
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69471
|
+
color: theme.colors.white,
|
|
69472
|
+
children: statusText
|
|
69473
|
+
}, undefined, false, undefined, this)
|
|
69474
|
+
]
|
|
69475
|
+
}, undefined, true, undefined, this);
|
|
69476
|
+
};
|
|
69477
|
+
var TypewriterText = ({ text, theme, speed = 2 }) => {
|
|
69478
|
+
const [displayedLength, setDisplayedLength] = import_react32.useState(0);
|
|
69479
|
+
const [cursorVisible, setCursorVisible] = import_react32.useState(true);
|
|
69480
|
+
const previousTextRef = import_react32.useRef("");
|
|
69481
|
+
import_react32.useEffect(() => {
|
|
69482
|
+
if (text !== previousTextRef.current) {
|
|
69483
|
+
setDisplayedLength(0);
|
|
69484
|
+
previousTextRef.current = text;
|
|
69485
|
+
}
|
|
69486
|
+
}, [text]);
|
|
69487
|
+
import_react32.useEffect(() => {
|
|
69488
|
+
if (displayedLength < text.length) {
|
|
69489
|
+
const charsToAdd = Math.min(speed, text.length - displayedLength);
|
|
69490
|
+
const timeout = setTimeout(() => {
|
|
69491
|
+
setDisplayedLength((prev) => Math.min(prev + charsToAdd, text.length));
|
|
69492
|
+
}, 5);
|
|
69493
|
+
return () => clearTimeout(timeout);
|
|
69494
|
+
}
|
|
69495
|
+
}, [displayedLength, text, speed]);
|
|
69496
|
+
import_react32.useEffect(() => {
|
|
69497
|
+
const interval = setInterval(() => {
|
|
69498
|
+
setCursorVisible((prev) => !prev);
|
|
69499
|
+
}, 400);
|
|
69500
|
+
return () => clearInterval(interval);
|
|
69501
|
+
}, []);
|
|
69502
|
+
const displayedText = text.slice(0, displayedLength);
|
|
69503
|
+
const isComplete = displayedLength >= text.length;
|
|
69504
|
+
const cursor = !isComplete && cursorVisible ? "█" : !isComplete ? " " : "";
|
|
69505
|
+
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
69506
|
+
flexDirection: "column",
|
|
69507
|
+
children: [
|
|
69508
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(JsonSyntaxHighlight, {
|
|
69509
|
+
jsonString: displayedText,
|
|
69510
|
+
theme
|
|
69511
|
+
}, undefined, false, undefined, this),
|
|
69512
|
+
!isComplete && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69513
|
+
color: theme.colors.accent,
|
|
69514
|
+
children: cursor
|
|
69515
|
+
}, undefined, false, undefined, this)
|
|
69516
|
+
]
|
|
69517
|
+
}, undefined, true, undefined, this);
|
|
69518
|
+
};
|
|
69371
69519
|
var ResponsePanel = import_react32.default.memo(({ response, theme }) => {
|
|
69372
69520
|
const [activeTab, setActiveTab] = import_react32.useState("body");
|
|
69373
69521
|
const tabs = [{ name: "body", label: "Body" }, { name: "headers", label: "Headers" }];
|
|
@@ -69377,25 +69525,12 @@ var ResponsePanel = import_react32.default.memo(({ response, theme }) => {
|
|
|
69377
69525
|
children: [
|
|
69378
69526
|
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
69379
69527
|
marginBottom: 1,
|
|
69380
|
-
children:
|
|
69381
|
-
|
|
69382
|
-
|
|
69383
|
-
|
|
69384
|
-
|
|
69385
|
-
|
|
69386
|
-
}, undefined, false, undefined, this)
|
|
69387
|
-
}, undefined, false, undefined, this),
|
|
69388
|
-
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
69389
|
-
color: getStatusColor(response.status, theme),
|
|
69390
|
-
bold: true,
|
|
69391
|
-
children: [
|
|
69392
|
-
response.status,
|
|
69393
|
-
" ",
|
|
69394
|
-
response.statustext
|
|
69395
|
-
]
|
|
69396
|
-
}, undefined, true, undefined, this)
|
|
69397
|
-
]
|
|
69398
|
-
}, undefined, true, undefined, this),
|
|
69528
|
+
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(StatusBadge, {
|
|
69529
|
+
status: response.status,
|
|
69530
|
+
statusText: response.statustext,
|
|
69531
|
+
theme
|
|
69532
|
+
}, undefined, false, undefined, this)
|
|
69533
|
+
}, undefined, false, undefined, this),
|
|
69399
69534
|
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Tabs, {
|
|
69400
69535
|
tabs,
|
|
69401
69536
|
activeTab,
|
|
@@ -69431,9 +69566,10 @@ var ResponsePanel = import_react32.default.memo(({ response, theme }) => {
|
|
|
69431
69566
|
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
69432
69567
|
flexDirection: "column",
|
|
69433
69568
|
flexGrow: 1,
|
|
69434
|
-
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(
|
|
69435
|
-
|
|
69436
|
-
theme
|
|
69569
|
+
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TypewriterText, {
|
|
69570
|
+
text: response.body,
|
|
69571
|
+
theme,
|
|
69572
|
+
speed: 50
|
|
69437
69573
|
}, undefined, false, undefined, this)
|
|
69438
69574
|
}, undefined, false, undefined, this)
|
|
69439
69575
|
}, undefined, false, undefined, this)
|
|
@@ -69872,42 +70008,100 @@ class ThemeManager {
|
|
|
69872
70008
|
var themeManager = new ThemeManager;
|
|
69873
70009
|
|
|
69874
70010
|
// src/ui/app/components/metricspanel.tsx
|
|
70011
|
+
var import_react34 = __toESM(require_react(), 1);
|
|
69875
70012
|
var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
|
|
69876
|
-
var
|
|
69877
|
-
|
|
70013
|
+
var BAR_CHARS = ["▱", "▰"];
|
|
70014
|
+
var GLOW_FRAMES = ["◐", "◓", "◑", "◒"];
|
|
70015
|
+
var SPARK_CHARS = ["⚡", "✦", "✧", "⚡", "★", "☆"];
|
|
70016
|
+
var MetricBar = ({ label, value, maxValue, color, theme, delay }) => {
|
|
70017
|
+
const barWidth = 30;
|
|
69878
70018
|
const safeValue = Math.max(0, value);
|
|
69879
|
-
const
|
|
69880
|
-
const
|
|
69881
|
-
const
|
|
70019
|
+
const targetFilled = Math.max(0, Math.min(barWidth, maxValue > 0 ? Math.round(safeValue / maxValue * barWidth) : 0));
|
|
70020
|
+
const [animatedFilled, setAnimatedFilled] = import_react34.useState(0);
|
|
70021
|
+
const [glowFrame, setGlowFrame] = import_react34.useState(0);
|
|
70022
|
+
const [sparkle, setSparkle] = import_react34.useState(false);
|
|
70023
|
+
import_react34.useEffect(() => {
|
|
70024
|
+
const timeout = setTimeout(() => {
|
|
70025
|
+
if (animatedFilled < targetFilled) {
|
|
70026
|
+
const interval = setInterval(() => {
|
|
70027
|
+
setAnimatedFilled((prev) => {
|
|
70028
|
+
if (prev >= targetFilled) {
|
|
70029
|
+
clearInterval(interval);
|
|
70030
|
+
setSparkle(true);
|
|
70031
|
+
setTimeout(() => setSparkle(false), 300);
|
|
70032
|
+
return targetFilled;
|
|
70033
|
+
}
|
|
70034
|
+
return prev + 1;
|
|
70035
|
+
});
|
|
70036
|
+
}, 20);
|
|
70037
|
+
return () => clearInterval(interval);
|
|
70038
|
+
}
|
|
70039
|
+
}, delay);
|
|
70040
|
+
return () => clearTimeout(timeout);
|
|
70041
|
+
}, [targetFilled, delay]);
|
|
70042
|
+
import_react34.useEffect(() => {
|
|
70043
|
+
const interval = setInterval(() => {
|
|
70044
|
+
setGlowFrame((prev) => (prev + 1) % GLOW_FRAMES.length);
|
|
70045
|
+
}, 150);
|
|
70046
|
+
return () => clearInterval(interval);
|
|
70047
|
+
}, []);
|
|
70048
|
+
const filledBar = BAR_CHARS[1].repeat(animatedFilled);
|
|
70049
|
+
const emptyBar = BAR_CHARS[0].repeat(barWidth - animatedFilled);
|
|
70050
|
+
const indicator = animatedFilled > 0 && animatedFilled < targetFilled ? GLOW_FRAMES[glowFrame] : sparkle ? SPARK_CHARS[Math.floor(Math.random() * SPARK_CHARS.length)] : "";
|
|
69882
70051
|
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69883
70052
|
marginY: 0,
|
|
69884
70053
|
children: [
|
|
69885
70054
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69886
|
-
width:
|
|
70055
|
+
width: 16,
|
|
69887
70056
|
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
69888
|
-
color
|
|
70057
|
+
color,
|
|
70058
|
+
bold: true,
|
|
69889
70059
|
children: label
|
|
69890
70060
|
}, undefined, false, undefined, this)
|
|
69891
70061
|
}, undefined, false, undefined, this),
|
|
70062
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70063
|
+
color: theme.colors.muted,
|
|
70064
|
+
children: "│"
|
|
70065
|
+
}, undefined, false, undefined, this),
|
|
69892
70066
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69893
|
-
width: barWidth +
|
|
69894
|
-
children:
|
|
69895
|
-
|
|
69896
|
-
|
|
69897
|
-
|
|
70067
|
+
width: barWidth + 3,
|
|
70068
|
+
children: [
|
|
70069
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70070
|
+
color,
|
|
70071
|
+
children: filledBar
|
|
70072
|
+
}, undefined, false, undefined, this),
|
|
70073
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70074
|
+
color,
|
|
70075
|
+
bold: true,
|
|
70076
|
+
children: indicator
|
|
70077
|
+
}, undefined, false, undefined, this),
|
|
70078
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70079
|
+
color: theme.colors.muted,
|
|
70080
|
+
dimColor: true,
|
|
70081
|
+
children: emptyBar
|
|
70082
|
+
}, undefined, false, undefined, this)
|
|
70083
|
+
]
|
|
70084
|
+
}, undefined, true, undefined, this),
|
|
70085
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70086
|
+
color: theme.colors.muted,
|
|
70087
|
+
children: "│"
|
|
69898
70088
|
}, undefined, false, undefined, this),
|
|
69899
70089
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69900
|
-
width:
|
|
70090
|
+
width: 10,
|
|
69901
70091
|
justifyContent: "flex-end",
|
|
69902
|
-
children:
|
|
69903
|
-
|
|
69904
|
-
|
|
69905
|
-
|
|
69906
|
-
safeValue.toFixed(1)
|
|
69907
|
-
|
|
69908
|
-
|
|
69909
|
-
|
|
69910
|
-
|
|
70092
|
+
children: [
|
|
70093
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70094
|
+
color: theme.colors.white,
|
|
70095
|
+
bold: true,
|
|
70096
|
+
children: safeValue.toFixed(1)
|
|
70097
|
+
}, undefined, false, undefined, this),
|
|
70098
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70099
|
+
color: theme.colors.muted,
|
|
70100
|
+
dimColor: true,
|
|
70101
|
+
children: "ms"
|
|
70102
|
+
}, undefined, false, undefined, this)
|
|
70103
|
+
]
|
|
70104
|
+
}, undefined, true, undefined, this)
|
|
69911
70105
|
]
|
|
69912
70106
|
}, undefined, true, undefined, this);
|
|
69913
70107
|
};
|
|
@@ -69919,75 +70113,290 @@ var formatBytes = (bytes) => {
|
|
|
69919
70113
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
69920
70114
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
69921
70115
|
};
|
|
69922
|
-
var
|
|
69923
|
-
|
|
69924
|
-
|
|
69925
|
-
|
|
69926
|
-
|
|
69927
|
-
|
|
69928
|
-
|
|
69929
|
-
|
|
69930
|
-
|
|
69931
|
-
|
|
69932
|
-
|
|
69933
|
-
|
|
70116
|
+
var AnimatedValue = ({ value, suffix, color }) => {
|
|
70117
|
+
const [displayValue, setDisplayValue] = import_react34.useState(0);
|
|
70118
|
+
import_react34.useEffect(() => {
|
|
70119
|
+
const duration = 500;
|
|
70120
|
+
const steps = 20;
|
|
70121
|
+
const increment = value / steps;
|
|
70122
|
+
let current = 0;
|
|
70123
|
+
const interval = setInterval(() => {
|
|
70124
|
+
current += increment;
|
|
70125
|
+
if (current >= value) {
|
|
70126
|
+
setDisplayValue(value);
|
|
70127
|
+
clearInterval(interval);
|
|
70128
|
+
} else {
|
|
70129
|
+
setDisplayValue(current);
|
|
70130
|
+
}
|
|
70131
|
+
}, duration / steps);
|
|
70132
|
+
return () => clearInterval(interval);
|
|
70133
|
+
}, [value]);
|
|
70134
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70135
|
+
color,
|
|
70136
|
+
bold: true,
|
|
70137
|
+
children: [
|
|
70138
|
+
displayValue.toFixed(2),
|
|
70139
|
+
" ",
|
|
70140
|
+
suffix
|
|
70141
|
+
]
|
|
70142
|
+
}, undefined, true, undefined, this);
|
|
70143
|
+
};
|
|
70144
|
+
var PulsingDot = ({ color }) => {
|
|
70145
|
+
const [frame, setFrame] = import_react34.useState(0);
|
|
70146
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
70147
|
+
import_react34.useEffect(() => {
|
|
70148
|
+
const interval = setInterval(() => {
|
|
70149
|
+
setFrame((prev) => (prev + 1) % frames.length);
|
|
70150
|
+
}, 80);
|
|
70151
|
+
return () => clearInterval(interval);
|
|
70152
|
+
}, []);
|
|
70153
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70154
|
+
color,
|
|
70155
|
+
children: frames[frame]
|
|
70156
|
+
}, undefined, false, undefined, this);
|
|
70157
|
+
};
|
|
70158
|
+
var TimelineWaterfall = ({ metrics, theme }) => {
|
|
70159
|
+
const [animationProgress, setAnimationProgress] = import_react34.useState(0);
|
|
70160
|
+
const [pulseFrame, setPulseFrame] = import_react34.useState(0);
|
|
70161
|
+
const totalWidth = 50;
|
|
70162
|
+
const segments = [
|
|
70163
|
+
{ label: "DNS", value: Math.max(0, metrics.dnsLookup), color: theme.colors.cool, icon: "⚡" },
|
|
70164
|
+
{ label: "TCP", value: Math.max(0, metrics.tcpConnection), color: theme.colors.success, icon: "\uD83D\uDD0C" },
|
|
70165
|
+
...metrics.tlsHandshake > 0 ? [{ label: "TLS", value: Math.max(0, metrics.tlsHandshake), color: theme.colors.secondary, icon: "\uD83D\uDD10" }] : [],
|
|
70166
|
+
{ label: "TTFB", value: Math.max(0, metrics.ttfb), color: theme.colors.accent, icon: "⏱️" },
|
|
70167
|
+
{ label: "DL", value: Math.max(0, metrics.contentDownload), color: theme.colors.primary, icon: "\uD83D\uDCE5" }
|
|
70168
|
+
];
|
|
70169
|
+
const totalTime = segments.reduce((sum, s) => sum + s.value, 0);
|
|
70170
|
+
import_react34.useEffect(() => {
|
|
70171
|
+
setAnimationProgress(0);
|
|
70172
|
+
const interval = setInterval(() => {
|
|
70173
|
+
setAnimationProgress((prev) => {
|
|
70174
|
+
if (prev >= 100) {
|
|
70175
|
+
clearInterval(interval);
|
|
70176
|
+
return 100;
|
|
70177
|
+
}
|
|
70178
|
+
return prev + 2;
|
|
70179
|
+
});
|
|
70180
|
+
}, 15);
|
|
70181
|
+
return () => clearInterval(interval);
|
|
70182
|
+
}, [metrics]);
|
|
70183
|
+
import_react34.useEffect(() => {
|
|
70184
|
+
const interval = setInterval(() => {
|
|
70185
|
+
setPulseFrame((prev) => (prev + 1) % 4);
|
|
70186
|
+
}, 200);
|
|
70187
|
+
return () => clearInterval(interval);
|
|
70188
|
+
}, []);
|
|
70189
|
+
const pulseChars = ["▁", "▂", "▃", "▄"];
|
|
70190
|
+
const endCaps = ["◀", "▶"];
|
|
69934
70191
|
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69935
70192
|
flexDirection: "column",
|
|
69936
|
-
|
|
70193
|
+
marginY: 1,
|
|
69937
70194
|
children: [
|
|
69938
70195
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69939
70196
|
marginBottom: 1,
|
|
69940
|
-
flexDirection: "column",
|
|
69941
70197
|
children: [
|
|
69942
70198
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
69943
70199
|
color: theme.colors.accent,
|
|
69944
70200
|
bold: true,
|
|
69945
|
-
children: "
|
|
70201
|
+
children: "⏳ Request Timeline"
|
|
69946
70202
|
}, undefined, false, undefined, this),
|
|
69947
70203
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
69948
70204
|
color: theme.colors.muted,
|
|
69949
|
-
children:
|
|
70205
|
+
children: [
|
|
70206
|
+
" (",
|
|
70207
|
+
totalTime.toFixed(0),
|
|
70208
|
+
"ms total)"
|
|
70209
|
+
]
|
|
70210
|
+
}, undefined, true, undefined, this)
|
|
70211
|
+
]
|
|
70212
|
+
}, undefined, true, undefined, this),
|
|
70213
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70214
|
+
children: [
|
|
70215
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70216
|
+
color: theme.colors.muted,
|
|
70217
|
+
children: endCaps[0]
|
|
70218
|
+
}, undefined, false, undefined, this),
|
|
70219
|
+
segments.map((segment, idx) => {
|
|
70220
|
+
const segmentWidth = Math.max(1, Math.round(segment.value / totalTime * totalWidth));
|
|
70221
|
+
const animatedWidth = Math.round(animationProgress / 100 * segmentWidth);
|
|
70222
|
+
const filledPart = "█".repeat(Math.max(0, animatedWidth));
|
|
70223
|
+
const emptyPart = "░".repeat(Math.max(0, segmentWidth - animatedWidth));
|
|
70224
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70225
|
+
children: [
|
|
70226
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70227
|
+
color: segment.color,
|
|
70228
|
+
children: filledPart
|
|
70229
|
+
}, undefined, false, undefined, this),
|
|
70230
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70231
|
+
color: segment.color,
|
|
70232
|
+
dimColor: true,
|
|
70233
|
+
children: emptyPart
|
|
70234
|
+
}, undefined, false, undefined, this)
|
|
70235
|
+
]
|
|
70236
|
+
}, idx, true, undefined, this);
|
|
70237
|
+
}),
|
|
70238
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70239
|
+
color: theme.colors.muted,
|
|
70240
|
+
children: endCaps[1]
|
|
70241
|
+
}, undefined, false, undefined, this),
|
|
70242
|
+
animationProgress < 100 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70243
|
+
color: theme.colors.accent,
|
|
70244
|
+
children: [
|
|
70245
|
+
" ",
|
|
70246
|
+
pulseChars[pulseFrame]
|
|
70247
|
+
]
|
|
70248
|
+
}, undefined, true, undefined, this),
|
|
70249
|
+
animationProgress >= 100 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70250
|
+
color: theme.colors.success,
|
|
70251
|
+
children: " ✓"
|
|
69950
70252
|
}, undefined, false, undefined, this)
|
|
69951
70253
|
]
|
|
69952
70254
|
}, undefined, true, undefined, this),
|
|
70255
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70256
|
+
marginTop: 1,
|
|
70257
|
+
flexWrap: "wrap",
|
|
70258
|
+
children: segments.map((segment, idx) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70259
|
+
marginRight: 2,
|
|
70260
|
+
children: [
|
|
70261
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70262
|
+
color: segment.color,
|
|
70263
|
+
children: "■"
|
|
70264
|
+
}, undefined, false, undefined, this),
|
|
70265
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70266
|
+
color: theme.colors.muted,
|
|
70267
|
+
children: [
|
|
70268
|
+
" ",
|
|
70269
|
+
segment.label,
|
|
70270
|
+
" "
|
|
70271
|
+
]
|
|
70272
|
+
}, undefined, true, undefined, this),
|
|
70273
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70274
|
+
color: theme.colors.white,
|
|
70275
|
+
bold: true,
|
|
70276
|
+
children: segment.value.toFixed(0)
|
|
70277
|
+
}, undefined, false, undefined, this),
|
|
70278
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70279
|
+
color: theme.colors.muted,
|
|
70280
|
+
dimColor: true,
|
|
70281
|
+
children: "ms"
|
|
70282
|
+
}, undefined, false, undefined, this)
|
|
70283
|
+
]
|
|
70284
|
+
}, idx, true, undefined, this))
|
|
70285
|
+
}, undefined, false, undefined, this)
|
|
70286
|
+
]
|
|
70287
|
+
}, undefined, true, undefined, this);
|
|
70288
|
+
};
|
|
70289
|
+
var MetricsPanel = ({ metrics, theme }) => {
|
|
70290
|
+
const [showContent, setShowContent] = import_react34.useState(false);
|
|
70291
|
+
import_react34.useEffect(() => {
|
|
70292
|
+
if (metrics) {
|
|
70293
|
+
setShowContent(false);
|
|
70294
|
+
const timeout = setTimeout(() => setShowContent(true), 100);
|
|
70295
|
+
return () => clearTimeout(timeout);
|
|
70296
|
+
}
|
|
70297
|
+
}, [metrics]);
|
|
70298
|
+
if (!metrics) {
|
|
70299
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70300
|
+
flexDirection: "column",
|
|
70301
|
+
padding: 1,
|
|
70302
|
+
alignItems: "center",
|
|
70303
|
+
children: [
|
|
70304
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70305
|
+
marginBottom: 1,
|
|
70306
|
+
children: [
|
|
70307
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(PulsingDot, {
|
|
70308
|
+
color: theme.colors.muted
|
|
70309
|
+
}, undefined, false, undefined, this),
|
|
70310
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70311
|
+
color: theme.colors.muted,
|
|
70312
|
+
children: " Waiting for request..."
|
|
70313
|
+
}, undefined, false, undefined, this)
|
|
70314
|
+
]
|
|
70315
|
+
}, undefined, true, undefined, this),
|
|
70316
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70317
|
+
color: theme.colors.muted,
|
|
70318
|
+
dimColor: true,
|
|
70319
|
+
children: "Send a request to see metrics"
|
|
70320
|
+
}, undefined, false, undefined, this)
|
|
70321
|
+
]
|
|
70322
|
+
}, undefined, true, undefined, this);
|
|
70323
|
+
}
|
|
70324
|
+
const maxTime = Math.max(Math.max(0, metrics.dnsLookup), Math.max(0, metrics.tcpConnection), Math.max(0, metrics.tlsHandshake), Math.max(0, metrics.ttfb), Math.max(0, metrics.contentDownload), 1);
|
|
70325
|
+
if (!showContent) {
|
|
70326
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70327
|
+
flexDirection: "column",
|
|
70328
|
+
padding: 1,
|
|
70329
|
+
alignItems: "center",
|
|
70330
|
+
children: [
|
|
70331
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(PulsingDot, {
|
|
70332
|
+
color: theme.colors.accent
|
|
70333
|
+
}, undefined, false, undefined, this),
|
|
70334
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70335
|
+
color: theme.colors.accent,
|
|
70336
|
+
children: " Analyzing..."
|
|
70337
|
+
}, undefined, false, undefined, this)
|
|
70338
|
+
]
|
|
70339
|
+
}, undefined, true, undefined, this);
|
|
70340
|
+
}
|
|
70341
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70342
|
+
flexDirection: "column",
|
|
70343
|
+
paddingX: 1,
|
|
70344
|
+
children: [
|
|
70345
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(TimelineWaterfall, {
|
|
70346
|
+
metrics,
|
|
70347
|
+
theme
|
|
70348
|
+
}, undefined, false, undefined, this),
|
|
70349
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70350
|
+
marginTop: 1,
|
|
70351
|
+
flexDirection: "column",
|
|
70352
|
+
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70353
|
+
color: theme.colors.muted,
|
|
70354
|
+
children: "┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈"
|
|
70355
|
+
}, undefined, false, undefined, this)
|
|
70356
|
+
}, undefined, false, undefined, this),
|
|
69953
70357
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
69954
70358
|
flexDirection: "column",
|
|
69955
70359
|
gap: 0,
|
|
69956
70360
|
children: [
|
|
69957
70361
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(MetricBar, {
|
|
69958
|
-
label: "
|
|
70362
|
+
label: "⚡ DNS",
|
|
69959
70363
|
value: metrics.dnsLookup,
|
|
69960
70364
|
maxValue: maxTime,
|
|
69961
70365
|
color: theme.colors.cool,
|
|
69962
|
-
theme
|
|
70366
|
+
theme,
|
|
70367
|
+
delay: 0
|
|
69963
70368
|
}, undefined, false, undefined, this),
|
|
69964
70369
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(MetricBar, {
|
|
69965
|
-
label: "\uD83D\uDD0C TCP
|
|
70370
|
+
label: "\uD83D\uDD0C TCP",
|
|
69966
70371
|
value: metrics.tcpConnection,
|
|
69967
70372
|
maxValue: maxTime,
|
|
69968
70373
|
color: theme.colors.success,
|
|
69969
|
-
theme
|
|
70374
|
+
theme,
|
|
70375
|
+
delay: 100
|
|
69970
70376
|
}, undefined, false, undefined, this),
|
|
69971
70377
|
metrics.tlsHandshake > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(MetricBar, {
|
|
69972
|
-
label: "\uD83D\uDD10 TLS
|
|
70378
|
+
label: "\uD83D\uDD10 TLS",
|
|
69973
70379
|
value: metrics.tlsHandshake,
|
|
69974
70380
|
maxValue: maxTime,
|
|
69975
70381
|
color: theme.colors.secondary,
|
|
69976
|
-
theme
|
|
70382
|
+
theme,
|
|
70383
|
+
delay: 200
|
|
69977
70384
|
}, undefined, false, undefined, this),
|
|
69978
70385
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(MetricBar, {
|
|
69979
70386
|
label: "⏱️ TTFB",
|
|
69980
70387
|
value: metrics.ttfb,
|
|
69981
70388
|
maxValue: maxTime,
|
|
69982
70389
|
color: theme.colors.accent,
|
|
69983
|
-
theme
|
|
70390
|
+
theme,
|
|
70391
|
+
delay: 300
|
|
69984
70392
|
}, undefined, false, undefined, this),
|
|
69985
70393
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(MetricBar, {
|
|
69986
70394
|
label: "\uD83D\uDCE5 Download",
|
|
69987
70395
|
value: metrics.contentDownload,
|
|
69988
70396
|
maxValue: maxTime,
|
|
69989
70397
|
color: theme.colors.primary,
|
|
69990
|
-
theme
|
|
70398
|
+
theme,
|
|
70399
|
+
delay: 400
|
|
69991
70400
|
}, undefined, false, undefined, this)
|
|
69992
70401
|
]
|
|
69993
70402
|
}, undefined, true, undefined, this),
|
|
@@ -69996,7 +70405,7 @@ var MetricsPanel = ({ metrics, theme }) => {
|
|
|
69996
70405
|
flexDirection: "column",
|
|
69997
70406
|
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
69998
70407
|
color: theme.colors.muted,
|
|
69999
|
-
children: "
|
|
70408
|
+
children: "┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈"
|
|
70000
70409
|
}, undefined, false, undefined, this)
|
|
70001
70410
|
}, undefined, false, undefined, this),
|
|
70002
70411
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
@@ -70007,30 +70416,25 @@ var MetricsPanel = ({ metrics, theme }) => {
|
|
|
70007
70416
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70008
70417
|
children: [
|
|
70009
70418
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70010
|
-
width:
|
|
70419
|
+
width: 12,
|
|
70011
70420
|
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70012
70421
|
color: theme.colors.accent,
|
|
70013
|
-
|
|
70014
|
-
children: "\uD83D\uDCCA Total Time"
|
|
70422
|
+
children: "⚡ Total"
|
|
70015
70423
|
}, undefined, false, undefined, this)
|
|
70016
70424
|
}, undefined, false, undefined, this),
|
|
70017
|
-
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(
|
|
70018
|
-
|
|
70019
|
-
|
|
70020
|
-
|
|
70021
|
-
|
|
70022
|
-
" ms"
|
|
70023
|
-
]
|
|
70024
|
-
}, undefined, true, undefined, this)
|
|
70425
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(AnimatedValue, {
|
|
70426
|
+
value: metrics.total,
|
|
70427
|
+
suffix: "ms",
|
|
70428
|
+
color: theme.colors.white
|
|
70429
|
+
}, undefined, false, undefined, this)
|
|
70025
70430
|
]
|
|
70026
70431
|
}, undefined, true, undefined, this),
|
|
70027
70432
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70028
70433
|
children: [
|
|
70029
70434
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70030
|
-
width:
|
|
70435
|
+
width: 12,
|
|
70031
70436
|
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70032
70437
|
color: theme.colors.accent,
|
|
70033
|
-
bold: true,
|
|
70034
70438
|
children: "\uD83D\uDCE6 Size"
|
|
70035
70439
|
}, undefined, false, undefined, this)
|
|
70036
70440
|
}, undefined, false, undefined, this),
|
|
@@ -70044,10 +70448,9 @@ var MetricsPanel = ({ metrics, theme }) => {
|
|
|
70044
70448
|
metrics.contentLength > 0 && metrics.contentDownload > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70045
70449
|
children: [
|
|
70046
70450
|
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
70047
|
-
width:
|
|
70451
|
+
width: 12,
|
|
70048
70452
|
children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
|
|
70049
70453
|
color: theme.colors.accent,
|
|
70050
|
-
bold: true,
|
|
70051
70454
|
children: "\uD83D\uDE80 Speed"
|
|
70052
70455
|
}, undefined, false, undefined, this)
|
|
70053
70456
|
}, undefined, false, undefined, this),
|
|
@@ -70062,31 +70465,135 @@ var MetricsPanel = ({ metrics, theme }) => {
|
|
|
70062
70465
|
]
|
|
70063
70466
|
}, undefined, true, undefined, this)
|
|
70064
70467
|
]
|
|
70065
|
-
}, undefined, true, undefined, this)
|
|
70066
|
-
|
|
70067
|
-
|
|
70068
|
-
|
|
70468
|
+
}, undefined, true, undefined, this)
|
|
70469
|
+
]
|
|
70470
|
+
}, undefined, true, undefined, this);
|
|
70471
|
+
};
|
|
70472
|
+
|
|
70473
|
+
// src/ui/app/ui.tsx
|
|
70474
|
+
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
70475
|
+
var formatBytes2 = (bytes) => {
|
|
70476
|
+
if (bytes === 0)
|
|
70477
|
+
return "0 B";
|
|
70478
|
+
const k = 1024;
|
|
70479
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
70480
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
70481
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
70482
|
+
};
|
|
70483
|
+
var LiveByteCounter = ({ progress, theme }) => {
|
|
70484
|
+
const [pulseFrame, setPulseFrame] = import_react35.useState(0);
|
|
70485
|
+
const pulseChars = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"];
|
|
70486
|
+
const arrowFrames = ["↓", "⬇", "↓", "⇣"];
|
|
70487
|
+
import_react35.useEffect(() => {
|
|
70488
|
+
const interval = setInterval(() => {
|
|
70489
|
+
setPulseFrame((prev) => (prev + 1) % pulseChars.length);
|
|
70490
|
+
}, 100);
|
|
70491
|
+
return () => clearInterval(interval);
|
|
70492
|
+
}, []);
|
|
70493
|
+
if (!progress)
|
|
70494
|
+
return null;
|
|
70495
|
+
const { bytesReceived, totalBytes, speed } = progress;
|
|
70496
|
+
const percentage = totalBytes > 0 ? Math.round(bytesReceived / totalBytes * 100) : 0;
|
|
70497
|
+
const progressBarWidth = 20;
|
|
70498
|
+
const filledWidth = totalBytes > 0 ? Math.round(bytesReceived / totalBytes * progressBarWidth) : 0;
|
|
70499
|
+
const filledBar = "█".repeat(filledWidth);
|
|
70500
|
+
const emptyBar = "░".repeat(progressBarWidth - filledWidth);
|
|
70501
|
+
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70502
|
+
flexDirection: "column",
|
|
70503
|
+
paddingX: 1,
|
|
70504
|
+
paddingY: 1,
|
|
70505
|
+
borderStyle: "round",
|
|
70506
|
+
borderColor: theme.accent,
|
|
70507
|
+
children: [
|
|
70508
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70509
|
+
marginBottom: 1,
|
|
70069
70510
|
children: [
|
|
70070
|
-
/* @__PURE__ */
|
|
70071
|
-
color: theme.
|
|
70072
|
-
|
|
70511
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70512
|
+
color: theme.accent,
|
|
70513
|
+
bold: true,
|
|
70514
|
+
children: [
|
|
70515
|
+
pulseChars[pulseFrame],
|
|
70516
|
+
" "
|
|
70517
|
+
]
|
|
70518
|
+
}, undefined, true, undefined, this),
|
|
70519
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70520
|
+
color: theme.primary,
|
|
70521
|
+
bold: true,
|
|
70522
|
+
children: "DOWNLOADING "
|
|
70073
70523
|
}, undefined, false, undefined, this),
|
|
70074
|
-
/* @__PURE__ */
|
|
70075
|
-
|
|
70076
|
-
children:
|
|
70077
|
-
color: theme.colors.muted,
|
|
70078
|
-
dimColor: true,
|
|
70079
|
-
children: "TTFB = Time To First Byte (DNS + TCP + TLS + Server Processing)"
|
|
70080
|
-
}, undefined, false, undefined, this)
|
|
70524
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70525
|
+
color: theme.accent,
|
|
70526
|
+
children: arrowFrames[pulseFrame % arrowFrames.length]
|
|
70081
70527
|
}, undefined, false, undefined, this)
|
|
70082
70528
|
]
|
|
70529
|
+
}, undefined, true, undefined, this),
|
|
70530
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70531
|
+
children: [
|
|
70532
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70533
|
+
color: theme.success,
|
|
70534
|
+
children: filledBar
|
|
70535
|
+
}, undefined, false, undefined, this),
|
|
70536
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70537
|
+
color: theme.muted,
|
|
70538
|
+
dimColor: true,
|
|
70539
|
+
children: emptyBar
|
|
70540
|
+
}, undefined, false, undefined, this),
|
|
70541
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70542
|
+
color: theme.white,
|
|
70543
|
+
bold: true,
|
|
70544
|
+
children: [
|
|
70545
|
+
" ",
|
|
70546
|
+
percentage,
|
|
70547
|
+
"%"
|
|
70548
|
+
]
|
|
70549
|
+
}, undefined, true, undefined, this)
|
|
70550
|
+
]
|
|
70551
|
+
}, undefined, true, undefined, this),
|
|
70552
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70553
|
+
marginTop: 1,
|
|
70554
|
+
children: [
|
|
70555
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70556
|
+
marginRight: 2,
|
|
70557
|
+
children: [
|
|
70558
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70559
|
+
color: theme.accent,
|
|
70560
|
+
children: "\uD83D\uDCE6 "
|
|
70561
|
+
}, undefined, false, undefined, this),
|
|
70562
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70563
|
+
color: theme.white,
|
|
70564
|
+
bold: true,
|
|
70565
|
+
children: formatBytes2(bytesReceived)
|
|
70566
|
+
}, undefined, false, undefined, this),
|
|
70567
|
+
totalBytes > 0 && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70568
|
+
color: theme.muted,
|
|
70569
|
+
children: [
|
|
70570
|
+
" / ",
|
|
70571
|
+
formatBytes2(totalBytes)
|
|
70572
|
+
]
|
|
70573
|
+
}, undefined, true, undefined, this)
|
|
70574
|
+
]
|
|
70575
|
+
}, undefined, true, undefined, this),
|
|
70576
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70577
|
+
children: [
|
|
70578
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70579
|
+
color: theme.success,
|
|
70580
|
+
children: "\uD83D\uDE80 "
|
|
70581
|
+
}, undefined, false, undefined, this),
|
|
70582
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
70583
|
+
color: theme.success,
|
|
70584
|
+
bold: true,
|
|
70585
|
+
children: [
|
|
70586
|
+
formatBytes2(speed),
|
|
70587
|
+
"/s"
|
|
70588
|
+
]
|
|
70589
|
+
}, undefined, true, undefined, this)
|
|
70590
|
+
]
|
|
70591
|
+
}, undefined, true, undefined, this)
|
|
70592
|
+
]
|
|
70083
70593
|
}, undefined, true, undefined, this)
|
|
70084
70594
|
]
|
|
70085
70595
|
}, undefined, true, undefined, this);
|
|
70086
70596
|
};
|
|
70087
|
-
|
|
70088
|
-
// src/ui/app/ui.tsx
|
|
70089
|
-
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
70090
70597
|
var SendButton = ({ onPress, loading, theme }) => {
|
|
70091
70598
|
const { isFocused } = use_focus_default();
|
|
70092
70599
|
use_input_default((_, key) => {
|
|
@@ -70108,7 +70615,7 @@ var SendButton = ({ onPress, loading, theme }) => {
|
|
|
70108
70615
|
}, undefined, false, undefined, this);
|
|
70109
70616
|
};
|
|
70110
70617
|
var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
70111
|
-
var RequestPanel =
|
|
70618
|
+
var RequestPanel = import_react35.default.memo(({ request, onMethodChange, onUrlChange, onHeadersChange, onBodyChange, onSend, loading, theme, historyUrls = [], onInputFocus }) => /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70112
70619
|
flexDirection: "column",
|
|
70113
70620
|
gap: 1,
|
|
70114
70621
|
flexGrow: 1,
|
|
@@ -70159,29 +70666,31 @@ var RequestPanel = import_react34.default.memo(({ request, onMethodChange, onUrl
|
|
|
70159
70666
|
]
|
|
70160
70667
|
}, undefined, true, undefined, this));
|
|
70161
70668
|
var UI = () => {
|
|
70162
|
-
const [theme, setTheme] =
|
|
70669
|
+
const [theme, setTheme] = import_react35.useState(themes.catppuccin);
|
|
70163
70670
|
const { exit } = use_app_default();
|
|
70164
|
-
const [activeTab, setActiveTab] =
|
|
70165
|
-
const [request, setRequest] =
|
|
70166
|
-
const [response, setResponse] =
|
|
70167
|
-
const [metrics, setMetrics] =
|
|
70168
|
-
const [history, setHistory] =
|
|
70169
|
-
const [loading, setLoading] =
|
|
70170
|
-
const
|
|
70671
|
+
const [activeTab, setActiveTab] = import_react35.useState("request");
|
|
70672
|
+
const [request, setRequest] = import_react35.useState({ method: "GET", url: "", headers: "", body: "" });
|
|
70673
|
+
const [response, setResponse] = import_react35.useState({ statustext: "", status: "", headers: "", body: "", error: "" });
|
|
70674
|
+
const [metrics, setMetrics] = import_react35.useState(null);
|
|
70675
|
+
const [history, setHistory] = import_react35.useState([]);
|
|
70676
|
+
const [loading, setLoading] = import_react35.useState(false);
|
|
70677
|
+
const [downloadProgress, setDownloadProgress] = import_react35.useState(null);
|
|
70678
|
+
const requestRef = import_react35.useRef(request);
|
|
70171
70679
|
requestRef.current = request;
|
|
70172
|
-
|
|
70680
|
+
import_react35.useEffect(() => {
|
|
70173
70681
|
const loadHistory = async () => setHistory((await historyManager.loadHistory()).entries);
|
|
70174
70682
|
loadHistory();
|
|
70175
70683
|
}, []);
|
|
70176
|
-
|
|
70684
|
+
import_react35.useEffect(() => {
|
|
70177
70685
|
const loadTheme = async () => {
|
|
70178
70686
|
const loadedTheme = await themeManager.loadCurrTheme();
|
|
70179
70687
|
setTheme(loadedTheme);
|
|
70180
70688
|
};
|
|
70181
70689
|
loadTheme();
|
|
70182
70690
|
}, []);
|
|
70183
|
-
const handleSend =
|
|
70691
|
+
const handleSend = import_react35.useCallback(async () => {
|
|
70184
70692
|
setLoading(true);
|
|
70693
|
+
setDownloadProgress(null);
|
|
70185
70694
|
const startTime = Date.now();
|
|
70186
70695
|
const currentRequest = requestRef.current;
|
|
70187
70696
|
try {
|
|
@@ -70203,7 +70712,8 @@ var UI = () => {
|
|
|
70203
70712
|
method: currentRequest.method,
|
|
70204
70713
|
url: currentRequest.url,
|
|
70205
70714
|
headers: parsedHeaders,
|
|
70206
|
-
body: reqBody
|
|
70715
|
+
body: reqBody,
|
|
70716
|
+
onProgress: (progress) => setDownloadProgress(progress)
|
|
70207
70717
|
});
|
|
70208
70718
|
const responseTime = Date.now() - startTime;
|
|
70209
70719
|
await historyManager.addEntry({ ...currentRequest }, res.status, responseTime);
|
|
@@ -70216,13 +70726,14 @@ var UI = () => {
|
|
|
70216
70726
|
setActiveTab("response");
|
|
70217
70727
|
} finally {
|
|
70218
70728
|
setLoading(false);
|
|
70729
|
+
setDownloadProgress(null);
|
|
70219
70730
|
}
|
|
70220
70731
|
}, []);
|
|
70221
70732
|
const handleThemeChange = (theme2) => {
|
|
70222
70733
|
themeManager.ChangeTheme(theme2);
|
|
70223
70734
|
setTheme(theme2);
|
|
70224
70735
|
};
|
|
70225
|
-
const handleHistoryClick =
|
|
70736
|
+
const handleHistoryClick = import_react35.useCallback((item) => {
|
|
70226
70737
|
setRequest({
|
|
70227
70738
|
method: item.method,
|
|
70228
70739
|
url: item.url,
|
|
@@ -70233,9 +70744,9 @@ var UI = () => {
|
|
|
70233
70744
|
}, []);
|
|
70234
70745
|
const tabs = [{ name: "request", label: "Request" }, { name: "response", label: "Response" }];
|
|
70235
70746
|
const activeIndex = tabs.findIndex((t) => t.name === activeTab);
|
|
70236
|
-
const [showThemeSelector, setShowThemeSelector] =
|
|
70237
|
-
const [showExportDialog, setShowExportDialog] =
|
|
70238
|
-
const [inputFocused, setInputFocused] =
|
|
70747
|
+
const [showThemeSelector, setShowThemeSelector] = import_react35.useState(false);
|
|
70748
|
+
const [showExportDialog, setShowExportDialog] = import_react35.useState(false);
|
|
70749
|
+
const [inputFocused, setInputFocused] = import_react35.useState(false);
|
|
70239
70750
|
use_input_default((input, key) => {
|
|
70240
70751
|
if (input === "q" && !showExportDialog)
|
|
70241
70752
|
exit();
|
|
@@ -70254,10 +70765,10 @@ var UI = () => {
|
|
|
70254
70765
|
if ((input === "e" || input === "E") && !key.ctrl && !key.meta && !inputFocused && !showThemeSelector)
|
|
70255
70766
|
setShowExportDialog((prev) => !prev);
|
|
70256
70767
|
}, { isActive: !showExportDialog });
|
|
70257
|
-
const onMethodChange =
|
|
70258
|
-
const onUrlChange =
|
|
70259
|
-
const onHeadersChange =
|
|
70260
|
-
const onBodyChange =
|
|
70768
|
+
const onMethodChange = import_react35.useCallback((method) => setRequest((r) => ({ ...r, method })), []);
|
|
70769
|
+
const onUrlChange = import_react35.useCallback((url) => setRequest((r) => ({ ...r, url })), []);
|
|
70770
|
+
const onHeadersChange = import_react35.useCallback((headers) => setRequest((r) => ({ ...r, headers })), []);
|
|
70771
|
+
const onBodyChange = import_react35.useCallback((body) => setRequest((r) => ({ ...r, body })), []);
|
|
70261
70772
|
const historyUrls = Array.from(new Set(history.map((h) => h.url))).filter(Boolean);
|
|
70262
70773
|
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70263
70774
|
padding: 1,
|
|
@@ -70415,6 +70926,14 @@ var UI = () => {
|
|
|
70415
70926
|
onChange: setActiveTab,
|
|
70416
70927
|
theme
|
|
70417
70928
|
}, undefined, false, undefined, this),
|
|
70929
|
+
downloadProgress && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70930
|
+
marginTop: 1,
|
|
70931
|
+
justifyContent: "center",
|
|
70932
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(LiveByteCounter, {
|
|
70933
|
+
progress: downloadProgress,
|
|
70934
|
+
theme: theme.colors
|
|
70935
|
+
}, undefined, false, undefined, this)
|
|
70936
|
+
}, undefined, false, undefined, this),
|
|
70418
70937
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
70419
70938
|
marginTop: 1,
|
|
70420
70939
|
flexDirection: "column",
|
|
@@ -70663,7 +71182,7 @@ async function mockApis() {
|
|
|
70663
71182
|
|
|
70664
71183
|
// src/index.ts
|
|
70665
71184
|
var program2 = new Command;
|
|
70666
|
-
program2.version("1.
|
|
71185
|
+
program2.version("1.4.0").description(import_chalk9.default.yellow("PostBoy CLI - Test your APIs with ease"));
|
|
70667
71186
|
program2.command("run").description("Run a test API request").action(testCommand);
|
|
70668
71187
|
program2.command("mock-list").description("List the mock API servers").action(mockApis);
|
|
70669
71188
|
program2.command("ui").description("UI for PostBoy").action(uiCommand);
|