postboy-tui 1.3.5 → 1.3.6

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 +415 -74
  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_react32 = __toESM(require_react(), 1);
67537
+ var import_react33 = __toESM(require_react(), 1);
67538
67538
 
67539
67539
  // src/utils/history.ts
67540
67540
  import { promises as fs2 } from "fs";
@@ -68423,7 +68423,7 @@ var Footer = import_react27.default.memo(({ theme }) => {
68423
68423
  color: theme.primary,
68424
68424
  children: "PostBoy"
68425
68425
  }, undefined, false, undefined, this),
68426
- " — [Q] Quit | [Ctrl+Enter] Send | [Ctrl+L/H] Switch Tabs | [T] Theme Menu | [Tab] Navigate"
68426
+ " — [Q] Quit | [Ctrl+Enter] Send | [Ctrl+L/H] Switch Tabs | [T] Theme | [E] Export | [Tab] Navigate"
68427
68427
  ]
68428
68428
  }, undefined, true, undefined, this)
68429
68429
  }, undefined, false, undefined, this);
@@ -68765,6 +68765,332 @@ var ResponsePanel = import_react31.default.memo(({ response, theme }) => {
68765
68765
  }, undefined, true, undefined, this);
68766
68766
  });
68767
68767
 
68768
+ // src/ui/app/components/exportdialog.tsx
68769
+ var import_react32 = __toESM(require_react(), 1);
68770
+
68771
+ // src/utils/export.ts
68772
+ import { writeFile, mkdir } from "fs";
68773
+ import { spawn } from "child_process";
68774
+ import { promisify } from "util";
68775
+ import { dirname } from "path";
68776
+ var writeFileAsync = promisify(writeFile);
68777
+ var mkdirAsync = promisify(mkdir);
68778
+ var toCurl = (request) => {
68779
+ const parts = ["curl"];
68780
+ parts.push(`-X ${request.method}`);
68781
+ parts.push(`'${request.url}'`);
68782
+ try {
68783
+ const headers = JSON.parse(request.headers || "{}");
68784
+ Object.entries(headers).forEach(([key, value]) => {
68785
+ parts.push(`-H '${key}: ${value}'`);
68786
+ });
68787
+ } catch {}
68788
+ if (request.body && request.method !== "GET") {
68789
+ parts.push(`-d '${request.body}'`);
68790
+ }
68791
+ return parts.join(" \\\n ");
68792
+ };
68793
+ var toFetch = (request) => {
68794
+ const options = {
68795
+ method: request.method
68796
+ };
68797
+ try {
68798
+ const headers = JSON.parse(request.headers || "{}");
68799
+ if (Object.keys(headers).length > 0) {
68800
+ options.headers = headers;
68801
+ }
68802
+ } catch {}
68803
+ if (request.body && request.method !== "GET") {
68804
+ options.body = request.body;
68805
+ }
68806
+ const optionsStr = JSON.stringify(options, null, 2);
68807
+ return `fetch('${request.url}', ${optionsStr})
68808
+ .then(response => response.json())
68809
+ .then(data => console.log(data))
68810
+ .catch(error => console.error('Error:', error));`;
68811
+ };
68812
+ var copyToClipboard = async (text) => {
68813
+ return new Promise((resolve) => {
68814
+ const platform2 = process.platform;
68815
+ const clipboardCmds = platform2 === "darwin" ? [{ cmd: "pbcopy", args: [] }] : platform2 === "win32" ? [{ cmd: "clip", args: [] }] : [
68816
+ { cmd: "wl-copy", args: [] },
68817
+ { cmd: "xclip", args: ["-selection", "clipboard"] },
68818
+ { cmd: "xsel", args: ["--clipboard", "--input"] }
68819
+ ];
68820
+ const tryClipboard = (index) => {
68821
+ if (index >= clipboardCmds.length) {
68822
+ resolve(false);
68823
+ return;
68824
+ }
68825
+ const item = clipboardCmds[index];
68826
+ try {
68827
+ const proc = spawn(item.cmd, item.args, { stdio: ["pipe", "ignore", "ignore"] });
68828
+ proc.stdin.write(text);
68829
+ proc.stdin.end();
68830
+ proc.on("close", (code) => {
68831
+ if (code === 0) {
68832
+ resolve(true);
68833
+ } else {
68834
+ tryClipboard(index + 1);
68835
+ }
68836
+ });
68837
+ proc.on("error", () => tryClipboard(index + 1));
68838
+ } catch {
68839
+ tryClipboard(index + 1);
68840
+ }
68841
+ };
68842
+ tryClipboard(0);
68843
+ });
68844
+ };
68845
+ var saveToFile = async (content, filePath) => {
68846
+ try {
68847
+ const dir = dirname(filePath);
68848
+ await mkdirAsync(dir, { recursive: true });
68849
+ await writeFileAsync(filePath, content, "utf-8");
68850
+ return true;
68851
+ } catch {
68852
+ return false;
68853
+ }
68854
+ };
68855
+
68856
+ // src/ui/app/components/exportdialog.tsx
68857
+ var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
68858
+ var EXPORT_DIR = `${process.env.HOME}/.postboy/exports`;
68859
+ var ExportDialog = ({ request, onClose, theme }) => {
68860
+ const [format, setFormat] = import_react32.useState("curl");
68861
+ const [action, setAction] = import_react32.useState("copy");
68862
+ const [showSavePrompt, setShowSavePrompt] = import_react32.useState(false);
68863
+ const [filePath, setFilePath] = import_react32.useState("");
68864
+ const [message, setMessage] = import_react32.useState("");
68865
+ const [activeField, setActiveField] = import_react32.useState("format");
68866
+ const getExportContent = () => {
68867
+ return format === "curl" ? toCurl(request) : toFetch(request);
68868
+ };
68869
+ const handleExport = async () => {
68870
+ const content = getExportContent();
68871
+ if (action === "copy") {
68872
+ const success = await copyToClipboard(content);
68873
+ setMessage(success ? "Copied to clipboard!" : "Failed to copy. Try saving to file.");
68874
+ setTimeout(onClose, 1500);
68875
+ } else {
68876
+ setShowSavePrompt(true);
68877
+ setActiveField("path");
68878
+ }
68879
+ };
68880
+ const handleSave = async () => {
68881
+ if (!filePath.trim()) {
68882
+ setMessage("Please enter a file path");
68883
+ return;
68884
+ }
68885
+ const content = getExportContent();
68886
+ const ext = format === "curl" ? ".sh" : ".js";
68887
+ const fileName = filePath.endsWith(ext) ? filePath : filePath + ext;
68888
+ const finalPath = `${EXPORT_DIR}/${fileName}`;
68889
+ const success = await saveToFile(content, finalPath);
68890
+ setMessage(success ? `✓ File saved to:
68891
+ ${finalPath}` : "Failed to save file");
68892
+ setTimeout(onClose, 3000);
68893
+ };
68894
+ use_input_default((input, key) => {
68895
+ if (key.escape) {
68896
+ if (showSavePrompt) {
68897
+ setShowSavePrompt(false);
68898
+ setActiveField("action");
68899
+ } else {
68900
+ onClose();
68901
+ }
68902
+ return;
68903
+ }
68904
+ if (showSavePrompt) {
68905
+ if (key.return) {
68906
+ handleSave();
68907
+ return;
68908
+ }
68909
+ if (key.backspace || key.delete) {
68910
+ setFilePath((p) => p.slice(0, -1));
68911
+ return;
68912
+ }
68913
+ if (!key.upArrow && !key.downArrow && !key.leftArrow && !key.rightArrow && !key.tab) {
68914
+ setFilePath((p) => p + input);
68915
+ }
68916
+ return;
68917
+ }
68918
+ if (key.tab) {
68919
+ setActiveField((f) => f === "format" ? "action" : "format");
68920
+ return;
68921
+ }
68922
+ if (key.return) {
68923
+ handleExport();
68924
+ return;
68925
+ }
68926
+ if (activeField === "format") {
68927
+ if (key.leftArrow || key.rightArrow || input === "h" || input === "l") {
68928
+ setFormat((f) => f === "curl" ? "fetch" : "curl");
68929
+ }
68930
+ } else if (activeField === "action") {
68931
+ if (key.leftArrow || key.rightArrow || input === "h" || input === "l") {
68932
+ setAction((a) => a === "copy" ? "save" : "copy");
68933
+ }
68934
+ }
68935
+ });
68936
+ const preview = getExportContent();
68937
+ return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68938
+ flexDirection: "column",
68939
+ borderStyle: "double",
68940
+ borderColor: theme.accent,
68941
+ padding: 1,
68942
+ children: [
68943
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68944
+ marginBottom: 1,
68945
+ children: [
68946
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68947
+ color: theme.accent,
68948
+ bold: true,
68949
+ children: "Export Request"
68950
+ }, undefined, false, undefined, this),
68951
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68952
+ color: theme.muted,
68953
+ children: " (Tab: switch, ←→: select, Enter: confirm, Esc: cancel)"
68954
+ }, undefined, false, undefined, this)
68955
+ ]
68956
+ }, undefined, true, undefined, this),
68957
+ message ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68958
+ padding: 1,
68959
+ flexDirection: "column",
68960
+ borderStyle: "round",
68961
+ borderColor: theme.success,
68962
+ children: message.split(`
68963
+ `).map((line, i) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68964
+ color: theme.success,
68965
+ bold: true,
68966
+ children: line
68967
+ }, i, false, undefined, this))
68968
+ }, undefined, false, undefined, this) : showSavePrompt ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68969
+ flexDirection: "column",
68970
+ gap: 1,
68971
+ children: [
68972
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68973
+ children: [
68974
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68975
+ color: theme.primary,
68976
+ children: "File path: "
68977
+ }, undefined, false, undefined, this),
68978
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
68979
+ borderStyle: "round",
68980
+ borderColor: theme.accent,
68981
+ paddingX: 1,
68982
+ flexGrow: 1,
68983
+ children: [
68984
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68985
+ color: theme.white,
68986
+ children: filePath
68987
+ }, undefined, false, undefined, this),
68988
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68989
+ color: theme.accent,
68990
+ children: "▌"
68991
+ }, undefined, false, undefined, this)
68992
+ ]
68993
+ }, undefined, true, undefined, this)
68994
+ ]
68995
+ }, undefined, true, undefined, this),
68996
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
68997
+ color: theme.muted,
68998
+ children: [
68999
+ "Extension .",
69000
+ format === "curl" ? "sh" : "js",
69001
+ " will be added if not specified"
69002
+ ]
69003
+ }, undefined, true, undefined, this)
69004
+ ]
69005
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69006
+ flexDirection: "column",
69007
+ gap: 1,
69008
+ children: [
69009
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69010
+ gap: 2,
69011
+ children: [
69012
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69013
+ color: theme.primary,
69014
+ children: "Format: "
69015
+ }, undefined, false, undefined, this),
69016
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69017
+ borderStyle: "round",
69018
+ borderColor: activeField === "format" && format === "curl" ? theme.accent : theme.muted,
69019
+ paddingX: 1,
69020
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69021
+ color: format === "curl" ? theme.accent : theme.muted,
69022
+ bold: format === "curl",
69023
+ children: "cURL"
69024
+ }, undefined, false, undefined, this)
69025
+ }, undefined, false, undefined, this),
69026
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69027
+ borderStyle: "round",
69028
+ borderColor: activeField === "format" && format === "fetch" ? theme.accent : theme.muted,
69029
+ paddingX: 1,
69030
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69031
+ color: format === "fetch" ? theme.accent : theme.muted,
69032
+ bold: format === "fetch",
69033
+ children: "Fetch"
69034
+ }, undefined, false, undefined, this)
69035
+ }, undefined, false, undefined, this)
69036
+ ]
69037
+ }, undefined, true, undefined, this),
69038
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69039
+ gap: 2,
69040
+ children: [
69041
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69042
+ color: theme.primary,
69043
+ children: "Action: "
69044
+ }, undefined, false, undefined, this),
69045
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69046
+ borderStyle: "round",
69047
+ borderColor: activeField === "action" && action === "copy" ? theme.accent : theme.muted,
69048
+ paddingX: 1,
69049
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69050
+ color: action === "copy" ? theme.accent : theme.muted,
69051
+ bold: action === "copy",
69052
+ children: "Copy to Clipboard"
69053
+ }, undefined, false, undefined, this)
69054
+ }, undefined, false, undefined, this),
69055
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69056
+ borderStyle: "round",
69057
+ borderColor: activeField === "action" && action === "save" ? theme.accent : theme.muted,
69058
+ paddingX: 1,
69059
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69060
+ color: action === "save" ? theme.accent : theme.muted,
69061
+ bold: action === "save",
69062
+ children: "Save to File"
69063
+ }, undefined, false, undefined, this)
69064
+ }, undefined, false, undefined, this)
69065
+ ]
69066
+ }, undefined, true, undefined, this),
69067
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69068
+ flexDirection: "column",
69069
+ marginTop: 1,
69070
+ children: [
69071
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69072
+ color: theme.primary,
69073
+ children: "Preview:"
69074
+ }, undefined, false, undefined, this),
69075
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69076
+ borderStyle: "round",
69077
+ borderColor: theme.muted,
69078
+ padding: 1,
69079
+ flexDirection: "column",
69080
+ children: preview.split(`
69081
+ `).map((line, i) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69082
+ color: theme.white,
69083
+ children: line
69084
+ }, i, false, undefined, this))
69085
+ }, undefined, false, undefined, this)
69086
+ ]
69087
+ }, undefined, true, undefined, this)
69088
+ ]
69089
+ }, undefined, true, undefined, this)
69090
+ ]
69091
+ }, undefined, true, undefined, this);
69092
+ };
69093
+
68768
69094
  // src/utils/themeManager.ts
68769
69095
  import { promises as fs3 } from "fs";
68770
69096
  import path2 from "path";
@@ -68868,21 +69194,21 @@ class ThemeManager {
68868
69194
  var themeManager = new ThemeManager;
68869
69195
 
68870
69196
  // src/ui/app/ui.tsx
68871
- var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
69197
+ var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
68872
69198
  var SendButton = ({ onPress, loading, theme }) => {
68873
69199
  const { isFocused } = use_focus_default();
68874
69200
  use_input_default((_, key) => {
68875
69201
  if (isFocused && key.return)
68876
69202
  onPress();
68877
69203
  });
68878
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69204
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
68879
69205
  borderStyle: "round",
68880
69206
  paddingX: 2,
68881
69207
  borderTopDimColor: true,
68882
69208
  borderColor: isFocused ? theme.accent : theme.primary,
68883
- children: loading ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Spinner, {
69209
+ children: loading ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Spinner, {
68884
69210
  theme
68885
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69211
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
68886
69212
  bold: true,
68887
69213
  color: isFocused ? theme.accent : theme.white,
68888
69214
  children: "\uD83D\uDE80 Send"
@@ -68890,12 +69216,12 @@ var SendButton = ({ onPress, loading, theme }) => {
68890
69216
  }, undefined, false, undefined, this);
68891
69217
  };
68892
69218
  var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
68893
- var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrlChange, onHeadersChange, onBodyChange, onSend, loading, theme, historyUrls = [], onInputFocus }) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69219
+ var RequestPanel = import_react33.default.memo(({ request, onMethodChange, onUrlChange, onHeadersChange, onBodyChange, onSend, loading, theme, historyUrls = [], onInputFocus }) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
68894
69220
  flexDirection: "column",
68895
69221
  gap: 1,
68896
69222
  flexGrow: 1,
68897
69223
  children: [
68898
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(FormField, {
69224
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(FormField, {
68899
69225
  label: "Method",
68900
69226
  value: request.method,
68901
69227
  onChange: onMethodChange,
@@ -68904,7 +69230,7 @@ var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrl
68904
69230
  suggestions: HTTP_METHODS,
68905
69231
  onFocusChange: onInputFocus
68906
69232
  }, undefined, false, undefined, this),
68907
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(FormField, {
69233
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(FormField, {
68908
69234
  label: "URL",
68909
69235
  value: request.url,
68910
69236
  onChange: onUrlChange,
@@ -68913,7 +69239,7 @@ var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrl
68913
69239
  suggestions: historyUrls,
68914
69240
  onFocusChange: onInputFocus
68915
69241
  }, undefined, false, undefined, this),
68916
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(KeyValueField, {
69242
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(KeyValueField, {
68917
69243
  label: "Headers",
68918
69244
  value: request.headers,
68919
69245
  onChange: onHeadersChange,
@@ -68921,7 +69247,7 @@ var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrl
68921
69247
  theme,
68922
69248
  onFocusChange: onInputFocus
68923
69249
  }, undefined, false, undefined, this),
68924
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(KeyValueField, {
69250
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(KeyValueField, {
68925
69251
  label: "Body",
68926
69252
  value: request.body,
68927
69253
  onChange: onBodyChange,
@@ -68929,10 +69255,10 @@ var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrl
68929
69255
  theme,
68930
69256
  onFocusChange: onInputFocus
68931
69257
  }, undefined, false, undefined, this),
68932
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69258
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
68933
69259
  marginTop: 1,
68934
69260
  justifyContent: "center",
68935
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(SendButton, {
69261
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(SendButton, {
68936
69262
  onPress: onSend,
68937
69263
  loading,
68938
69264
  theme
@@ -68941,27 +69267,27 @@ var RequestPanel = import_react32.default.memo(({ request, onMethodChange, onUrl
68941
69267
  ]
68942
69268
  }, undefined, true, undefined, this));
68943
69269
  var UI = () => {
68944
- const [theme, setTheme] = import_react32.useState(themes.catppuccin);
69270
+ const [theme, setTheme] = import_react33.useState(themes.catppuccin);
68945
69271
  const { exit } = use_app_default();
68946
- const [activeTab, setActiveTab] = import_react32.useState("request");
68947
- const [request, setRequest] = import_react32.useState({ method: "GET", url: "", headers: "", body: "" });
68948
- const [response, setResponse] = import_react32.useState({ statustext: "", status: "", headers: "", body: "", error: "" });
68949
- const [history, setHistory] = import_react32.useState([]);
68950
- const [loading, setLoading] = import_react32.useState(false);
68951
- const requestRef = import_react32.useRef(request);
69272
+ const [activeTab, setActiveTab] = import_react33.useState("request");
69273
+ const [request, setRequest] = import_react33.useState({ method: "GET", url: "", headers: "", body: "" });
69274
+ const [response, setResponse] = import_react33.useState({ statustext: "", status: "", headers: "", body: "", error: "" });
69275
+ const [history, setHistory] = import_react33.useState([]);
69276
+ const [loading, setLoading] = import_react33.useState(false);
69277
+ const requestRef = import_react33.useRef(request);
68952
69278
  requestRef.current = request;
68953
- import_react32.useEffect(() => {
69279
+ import_react33.useEffect(() => {
68954
69280
  const loadHistory = async () => setHistory((await historyManager.loadHistory()).entries);
68955
69281
  loadHistory();
68956
69282
  }, []);
68957
- import_react32.useEffect(() => {
69283
+ import_react33.useEffect(() => {
68958
69284
  const loadTheme = async () => {
68959
69285
  const loadedTheme = await themeManager.loadCurrTheme();
68960
69286
  setTheme(loadedTheme);
68961
69287
  };
68962
69288
  loadTheme();
68963
69289
  }, []);
68964
- const handleSend = import_react32.useCallback(async () => {
69290
+ const handleSend = import_react33.useCallback(async () => {
68965
69291
  setLoading(true);
68966
69292
  const startTime = Date.now();
68967
69293
  const currentRequest = requestRef.current;
@@ -69002,7 +69328,7 @@ var UI = () => {
69002
69328
  themeManager.ChangeTheme(theme2);
69003
69329
  setTheme(theme2);
69004
69330
  };
69005
- const handleHistoryClick = import_react32.useCallback((item) => {
69331
+ const handleHistoryClick = import_react33.useCallback((item) => {
69006
69332
  setRequest({
69007
69333
  method: item.method,
69008
69334
  url: item.url,
@@ -69013,10 +69339,11 @@ var UI = () => {
69013
69339
  }, []);
69014
69340
  const tabs = [{ name: "request", label: "Request" }, { name: "response", label: "Response" }];
69015
69341
  const activeIndex = tabs.findIndex((t) => t.name === activeTab);
69016
- const [showThemeSelector, setShowThemeSelector] = import_react32.useState(false);
69017
- const [inputFocused, setInputFocused] = import_react32.useState(false);
69342
+ const [showThemeSelector, setShowThemeSelector] = import_react33.useState(false);
69343
+ const [showExportDialog, setShowExportDialog] = import_react33.useState(false);
69344
+ const [inputFocused, setInputFocused] = import_react33.useState(false);
69018
69345
  use_input_default((input, key) => {
69019
- if (input === "q")
69346
+ if (input === "q" && !showExportDialog)
69020
69347
  exit();
69021
69348
  if (key.ctrl && key.return)
69022
69349
  handleSend();
@@ -69026,87 +69353,101 @@ var UI = () => {
69026
69353
  setActiveTab(tabs[(activeIndex - 1 + tabs.length) % tabs.length]?.name ?? "request");
69027
69354
  if (key.escape && showThemeSelector)
69028
69355
  setShowThemeSelector(false);
69029
- if ((input === "t" || input === "T") && !key.ctrl && !key.meta && !inputFocused)
69356
+ if (key.escape && showExportDialog)
69357
+ setShowExportDialog(false);
69358
+ if ((input === "t" || input === "T") && !key.ctrl && !key.meta && !inputFocused && !showExportDialog)
69030
69359
  setShowThemeSelector((prev) => !prev);
69031
- }, { isActive: true });
69032
- const onMethodChange = import_react32.useCallback((method) => setRequest((r) => ({ ...r, method })), []);
69033
- const onUrlChange = import_react32.useCallback((url) => setRequest((r) => ({ ...r, url })), []);
69034
- const onHeadersChange = import_react32.useCallback((headers) => setRequest((r) => ({ ...r, headers })), []);
69035
- const onBodyChange = import_react32.useCallback((body) => setRequest((r) => ({ ...r, body })), []);
69360
+ if ((input === "e" || input === "E") && !key.ctrl && !key.meta && !inputFocused && !showThemeSelector)
69361
+ setShowExportDialog((prev) => !prev);
69362
+ }, { isActive: !showExportDialog });
69363
+ const onMethodChange = import_react33.useCallback((method) => setRequest((r) => ({ ...r, method })), []);
69364
+ const onUrlChange = import_react33.useCallback((url) => setRequest((r) => ({ ...r, url })), []);
69365
+ const onHeadersChange = import_react33.useCallback((headers) => setRequest((r) => ({ ...r, headers })), []);
69366
+ const onBodyChange = import_react33.useCallback((body) => setRequest((r) => ({ ...r, body })), []);
69036
69367
  const historyUrls = Array.from(new Set(history.map((h) => h.url))).filter(Boolean);
69037
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69368
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69038
69369
  padding: 1,
69039
69370
  flexDirection: "column",
69040
69371
  flexGrow: 1,
69041
69372
  children: [
69042
- showThemeSelector && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69373
+ showThemeSelector && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69043
69374
  flexDirection: "row",
69044
69375
  justifyContent: "center",
69045
69376
  marginBottom: 1,
69046
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ThemeSelector, {
69377
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ThemeSelector, {
69047
69378
  theme,
69048
69379
  onThemeChange: (themeName) => {
69049
69380
  handleThemeChange(themes[themeName]);
69050
69381
  }
69051
69382
  }, undefined, false, undefined, this)
69052
69383
  }, undefined, false, undefined, this),
69053
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69384
+ showExportDialog && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69385
+ flexDirection: "row",
69386
+ justifyContent: "center",
69387
+ marginBottom: 1,
69388
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ExportDialog, {
69389
+ request,
69390
+ onClose: () => setShowExportDialog(false),
69391
+ theme: theme.colors
69392
+ }, undefined, false, undefined, this)
69393
+ }, undefined, false, undefined, this),
69394
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69054
69395
  alignSelf: "center",
69055
69396
  marginBottom: 1,
69056
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69397
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69057
69398
  color: theme.colors.accent,
69058
69399
  bold: true,
69059
69400
  children: `┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓`
69060
69401
  }, undefined, false, undefined, this)
69061
69402
  }, undefined, false, undefined, this),
69062
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69403
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69063
69404
  alignSelf: "center",
69064
69405
  marginBottom: 1,
69065
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69406
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69066
69407
  color: theme.colors.primary,
69067
69408
  bold: true,
69068
69409
  children: `┃ \uD83D\uDEF0️ Welcome to PostBoy — The Modern Terminal API Client ┃`
69069
69410
  }, undefined, false, undefined, this)
69070
69411
  }, undefined, false, undefined, this),
69071
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69412
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69072
69413
  alignSelf: "center",
69073
69414
  marginBottom: 1,
69074
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69415
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69075
69416
  color: theme.colors.accent,
69076
69417
  bold: true,
69077
69418
  children: `┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛`
69078
69419
  }, undefined, false, undefined, this)
69079
69420
  }, undefined, false, undefined, this),
69080
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69421
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69081
69422
  flexGrow: 1,
69082
69423
  children: [
69083
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69424
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69084
69425
  width: "40%",
69085
69426
  borderColor: theme.colors.muted,
69086
69427
  flexDirection: "column",
69087
69428
  marginRight: 1,
69088
69429
  children: [
69089
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69430
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69090
69431
  alignSelf: "center",
69091
69432
  marginBottom: 1,
69092
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69433
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69093
69434
  color: theme.colors.accent,
69094
69435
  bold: true,
69095
69436
  children: `┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓`
69096
69437
  }, undefined, false, undefined, this)
69097
69438
  }, undefined, false, undefined, this),
69098
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69439
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69099
69440
  borderTopColor: "grey",
69100
69441
  borderColor: theme.colors.secondary,
69101
69442
  paddingX: 1,
69102
69443
  alignSelf: "center",
69103
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69444
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69104
69445
  color: theme.colors.accent,
69105
69446
  bold: true,
69106
69447
  children: "\uD83D\uDCDC History"
69107
69448
  }, undefined, false, undefined, this)
69108
69449
  }, undefined, false, undefined, this),
69109
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69450
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69110
69451
  flexDirection: "column",
69111
69452
  flexGrow: 1,
69112
69453
  borderRightColor: "grey",
@@ -69115,22 +69456,22 @@ var UI = () => {
69115
69456
  borderLeft: false,
69116
69457
  borderBottom: false,
69117
69458
  paddingY: 1,
69118
- children: history.length === 0 ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69459
+ children: history.length === 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69119
69460
  padding: 1,
69120
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69461
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69121
69462
  color: theme.colors.muted,
69122
69463
  children: "No requests yet..."
69123
69464
  }, undefined, false, undefined, this)
69124
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(HistoryList, {
69465
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(HistoryList, {
69125
69466
  history,
69126
69467
  onItemClick: handleHistoryClick,
69127
69468
  theme
69128
69469
  }, undefined, false, undefined, this)
69129
69470
  }, undefined, false, undefined, this),
69130
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69471
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69131
69472
  alignSelf: "center",
69132
69473
  marginBottom: 1,
69133
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69474
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69134
69475
  color: theme.colors.accent,
69135
69476
  bold: true,
69136
69477
  children: `┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛`
@@ -69138,36 +69479,36 @@ var UI = () => {
69138
69479
  }, undefined, false, undefined, this)
69139
69480
  ]
69140
69481
  }, undefined, true, undefined, this),
69141
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69482
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69142
69483
  width: "60%",
69143
69484
  borderColor: theme.colors.muted,
69144
69485
  padding: 1,
69145
69486
  flexDirection: "column",
69146
69487
  children: [
69147
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69488
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69148
69489
  alignSelf: "center",
69149
69490
  marginBottom: 1,
69150
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69491
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69151
69492
  color: theme.colors.accent,
69152
69493
  bold: true,
69153
69494
  children: `┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓`
69154
69495
  }, undefined, false, undefined, this)
69155
69496
  }, undefined, false, undefined, this),
69156
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Tabs, {
69497
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Tabs, {
69157
69498
  tabs,
69158
69499
  activeTab,
69159
69500
  onChange: setActiveTab,
69160
69501
  theme
69161
69502
  }, undefined, false, undefined, this),
69162
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69503
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69163
69504
  marginTop: 1,
69164
69505
  flexDirection: "column",
69165
69506
  flexGrow: 1,
69166
69507
  children: [
69167
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69508
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69168
69509
  display: activeTab === "request" ? "flex" : "none",
69169
69510
  flexGrow: 1,
69170
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(RequestPanel, {
69511
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(RequestPanel, {
69171
69512
  request,
69172
69513
  onMethodChange,
69173
69514
  onUrlChange,
@@ -69180,20 +69521,20 @@ var UI = () => {
69180
69521
  onInputFocus: setInputFocused
69181
69522
  }, undefined, false, undefined, this)
69182
69523
  }, undefined, false, undefined, this),
69183
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69524
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69184
69525
  display: activeTab === "response" ? "flex" : "none",
69185
69526
  flexGrow: 1,
69186
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ResponsePanel, {
69527
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ResponsePanel, {
69187
69528
  response,
69188
69529
  theme
69189
69530
  }, undefined, false, undefined, this)
69190
69531
  }, undefined, false, undefined, this)
69191
69532
  ]
69192
69533
  }, undefined, true, undefined, this),
69193
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
69534
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
69194
69535
  alignSelf: "center",
69195
69536
  marginBottom: 1,
69196
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
69537
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
69197
69538
  color: theme.colors.accent,
69198
69539
  bold: true,
69199
69540
  children: `┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛`
@@ -69203,7 +69544,7 @@ var UI = () => {
69203
69544
  }, undefined, true, undefined, this)
69204
69545
  ]
69205
69546
  }, undefined, true, undefined, this),
69206
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Footer, {
69547
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Footer, {
69207
69548
  theme: theme.colors
69208
69549
  }, undefined, false, undefined, this)
69209
69550
  ]
@@ -69212,14 +69553,14 @@ var UI = () => {
69212
69553
  var ui_default = UI;
69213
69554
 
69214
69555
  // src/ui/app/app.tsx
69215
- var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
69556
+ var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
69216
69557
  function App2() {
69217
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ui_default, {}, undefined, false, undefined, this);
69558
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ui_default, {}, undefined, false, undefined, this);
69218
69559
  }
69219
69560
 
69220
69561
  // src/commands/ui.tsx
69221
69562
  var import_chalk5 = __toESM(require_source(), 1);
69222
- var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
69563
+ var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
69223
69564
  var UIWrapper = () => {
69224
69565
  const { exit } = use_app_default();
69225
69566
  use_input_default((input) => {
@@ -69227,18 +69568,18 @@ var UIWrapper = () => {
69227
69568
  exit();
69228
69569
  }
69229
69570
  });
69230
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
69571
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
69231
69572
  flexDirection: "column",
69232
69573
  children: [
69233
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
69574
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
69234
69575
  children: import_chalk5.default.cyanBright(`PostBoy \uD83D\uDC8C`)
69235
69576
  }, undefined, false, undefined, this),
69236
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(App2, {}, undefined, false, undefined, this)
69577
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(App2, {}, undefined, false, undefined, this)
69237
69578
  ]
69238
69579
  }, undefined, true, undefined, this);
69239
69580
  };
69240
69581
  var uiCommand = () => {
69241
- render_default(/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(UIWrapper, {}, undefined, false, undefined, this));
69582
+ render_default(/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(UIWrapper, {}, undefined, false, undefined, this));
69242
69583
  };
69243
69584
 
69244
69585
  // src/commands/test.ts
@@ -69407,7 +69748,7 @@ async function mockApis() {
69407
69748
 
69408
69749
  // src/index.ts
69409
69750
  var program2 = new Command;
69410
- program2.version("1.3.5").description(import_chalk8.default.yellow("PostBoy CLI - Test your APIs with ease"));
69751
+ program2.version("1.3.6").description(import_chalk8.default.yellow("PostBoy CLI - Test your APIs with ease"));
69411
69752
  program2.command("run").description("Run a test API request").action(testCommand);
69412
69753
  program2.command("mock-list").description("List the mock API servers").action(mockApis);
69413
69754
  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.5",
3
+ "version": "1.3.6",
4
4
  "main": "dist/cli.js",
5
5
  "bin": {
6
6
  "postboy-tui": "dist/cli.js"