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.
Files changed (2) hide show
  1. package/dist/cli.js +645 -126
  2. 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 import_react34 = __toESM(require_react(), 1);
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 = parseInt(res.headers["content-length"] || "0", 10) || Buffer.byteLength(data, "utf8");
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
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
69382
- width: 8,
69383
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
69384
- color: theme.colors.primary,
69385
- children: "STATUS:"
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(JsonSyntaxHighlight, {
69435
- jsonString: response.body,
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 MetricBar = ({ label, value, maxValue, color, theme }) => {
69877
- const barWidth = 40;
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 filledWidth = Math.max(0, Math.min(barWidth, maxValue > 0 ? Math.round(safeValue / maxValue * barWidth) : 0));
69880
- const emptyWidth = barWidth - filledWidth;
69881
- const bar = "█".repeat(filledWidth) + "░".repeat(emptyWidth);
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: 18,
70055
+ width: 16,
69887
70056
  children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69888
- color: theme.colors.muted,
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 + 2,
69894
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69895
- color,
69896
- children: bar
69897
- }, undefined, false, undefined, this)
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: 12,
70090
+ width: 10,
69901
70091
  justifyContent: "flex-end",
69902
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69903
- color: theme.colors.white,
69904
- bold: true,
69905
- children: [
69906
- safeValue.toFixed(1),
69907
- "ms"
69908
- ]
69909
- }, undefined, true, undefined, this)
69910
- }, undefined, false, undefined, this)
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 MetricsPanel = ({ metrics, theme }) => {
69923
- if (!metrics) {
69924
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69925
- flexDirection: "column",
69926
- padding: 1,
69927
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69928
- color: theme.colors.muted,
69929
- children: "No metrics available. Send a request first."
69930
- }, undefined, false, undefined, this)
69931
- }, undefined, false, undefined, this);
69932
- }
69933
- 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);
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
- paddingX: 1,
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: " Performance Breakdown"
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: "\uD83D\uDD0D DNS Lookup",
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 Connect",
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 Handshake",
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: 18,
70419
+ width: 12,
70011
70420
  children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
70012
70421
  color: theme.colors.accent,
70013
- bold: true,
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(Text, {
70018
- color: theme.colors.white,
70019
- bold: true,
70020
- children: [
70021
- metrics.total.toFixed(2),
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: 18,
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: 18,
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
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
70067
- marginTop: 1,
70068
- flexDirection: "column",
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__ */ jsx_dev_runtime12.jsxDEV(Text, {
70071
- color: theme.colors.muted,
70072
- children: "────────────────────────────────────────────────────────────"
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__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
70075
- marginTop: 1,
70076
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
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 = import_react34.default.memo(({ request, onMethodChange, onUrlChange, onHeadersChange, onBodyChange, onSend, loading, theme, historyUrls = [], onInputFocus }) => /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
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] = import_react34.useState(themes.catppuccin);
70669
+ const [theme, setTheme] = import_react35.useState(themes.catppuccin);
70163
70670
  const { exit } = use_app_default();
70164
- const [activeTab, setActiveTab] = import_react34.useState("request");
70165
- const [request, setRequest] = import_react34.useState({ method: "GET", url: "", headers: "", body: "" });
70166
- const [response, setResponse] = import_react34.useState({ statustext: "", status: "", headers: "", body: "", error: "" });
70167
- const [metrics, setMetrics] = import_react34.useState(null);
70168
- const [history, setHistory] = import_react34.useState([]);
70169
- const [loading, setLoading] = import_react34.useState(false);
70170
- const requestRef = import_react34.useRef(request);
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
- import_react34.useEffect(() => {
70680
+ import_react35.useEffect(() => {
70173
70681
  const loadHistory = async () => setHistory((await historyManager.loadHistory()).entries);
70174
70682
  loadHistory();
70175
70683
  }, []);
70176
- import_react34.useEffect(() => {
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 = import_react34.useCallback(async () => {
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 = import_react34.useCallback((item) => {
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] = import_react34.useState(false);
70237
- const [showExportDialog, setShowExportDialog] = import_react34.useState(false);
70238
- const [inputFocused, setInputFocused] = import_react34.useState(false);
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 = import_react34.useCallback((method) => setRequest((r) => ({ ...r, method })), []);
70258
- const onUrlChange = import_react34.useCallback((url) => setRequest((r) => ({ ...r, url })), []);
70259
- const onHeadersChange = import_react34.useCallback((headers) => setRequest((r) => ({ ...r, headers })), []);
70260
- const onBodyChange = import_react34.useCallback((body) => setRequest((r) => ({ ...r, body })), []);
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.3.9").description(import_chalk9.default.yellow("PostBoy CLI - Test your APIs with ease"));
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postboy-tui",
3
- "version": "1.3.9",
3
+ "version": "1.4.0",
4
4
  "main": "dist/cli.js",
5
5
  "bin": {
6
6
  "postboy-tui": "dist/cli.js"