htpx-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +274 -0
- package/dist/cli/commands/clear.d.ts +3 -0
- package/dist/cli/commands/clear.d.ts.map +1 -0
- package/dist/cli/commands/clear.js +30 -0
- package/dist/cli/commands/clear.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +25 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/intercept.d.ts +8 -0
- package/dist/cli/commands/intercept.d.ts.map +1 -0
- package/dist/cli/commands/intercept.js +59 -0
- package/dist/cli/commands/intercept.js.map +1 -0
- package/dist/cli/commands/project.d.ts +3 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +13 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +36 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +27 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/tui.d.ts +3 -0
- package/dist/cli/commands/tui.d.ts.map +1 -0
- package/dist/cli/commands/tui.js +27 -0
- package/dist/cli/commands/tui.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui/App.d.ts +10 -0
- package/dist/cli/tui/App.d.ts.map +1 -0
- package/dist/cli/tui/App.js +96 -0
- package/dist/cli/tui/App.js.map +1 -0
- package/dist/cli/tui/components/BodyView.d.ts +14 -0
- package/dist/cli/tui/components/BodyView.d.ts.map +1 -0
- package/dist/cli/tui/components/BodyView.js +39 -0
- package/dist/cli/tui/components/BodyView.js.map +1 -0
- package/dist/cli/tui/components/HeadersView.d.ts +13 -0
- package/dist/cli/tui/components/HeadersView.d.ts.map +1 -0
- package/dist/cli/tui/components/HeadersView.js +8 -0
- package/dist/cli/tui/components/HeadersView.js.map +1 -0
- package/dist/cli/tui/components/RequestDetails.d.ts +13 -0
- package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestDetails.js +19 -0
- package/dist/cli/tui/components/RequestDetails.js.map +1 -0
- package/dist/cli/tui/components/RequestList.d.ts +15 -0
- package/dist/cli/tui/components/RequestList.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestList.js +17 -0
- package/dist/cli/tui/components/RequestList.js.map +1 -0
- package/dist/cli/tui/components/RequestListItem.d.ts +13 -0
- package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestListItem.js +53 -0
- package/dist/cli/tui/components/RequestListItem.js.map +1 -0
- package/dist/cli/tui/components/StatusBar.d.ts +10 -0
- package/dist/cli/tui/components/StatusBar.d.ts.map +1 -0
- package/dist/cli/tui/components/StatusBar.js +18 -0
- package/dist/cli/tui/components/StatusBar.js.map +1 -0
- package/dist/cli/tui/hooks/useExport.d.ts +18 -0
- package/dist/cli/tui/hooks/useExport.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useExport.js +58 -0
- package/dist/cli/tui/hooks/useExport.js.map +1 -0
- package/dist/cli/tui/hooks/useRequests.d.ts +20 -0
- package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useRequests.js +85 -0
- package/dist/cli/tui/hooks/useRequests.js.map +1 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts +11 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.js +29 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.js.map +1 -0
- package/dist/cli/tui/utils/curl.d.ts +9 -0
- package/dist/cli/tui/utils/curl.d.ts.map +1 -0
- package/dist/cli/tui/utils/curl.js +47 -0
- package/dist/cli/tui/utils/curl.js.map +1 -0
- package/dist/cli/tui/utils/formatters.d.ts +36 -0
- package/dist/cli/tui/utils/formatters.d.ts.map +1 -0
- package/dist/cli/tui/utils/formatters.js +108 -0
- package/dist/cli/tui/utils/formatters.js.map +1 -0
- package/dist/cli/tui/utils/har.d.ts +75 -0
- package/dist/cli/tui/utils/har.d.ts.map +1 -0
- package/dist/cli/tui/utils/har.js +139 -0
- package/dist/cli/tui/utils/har.js.map +1 -0
- package/dist/daemon/control.d.ts +63 -0
- package/dist/daemon/control.d.ts.map +1 -0
- package/dist/daemon/control.js +260 -0
- package/dist/daemon/control.js.map +1 -0
- package/dist/daemon/index.d.ts +3 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +107 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/proxy.d.ts +19 -0
- package/dist/daemon/proxy.d.ts.map +1 -0
- package/dist/daemon/proxy.js +89 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/storage.d.ts +60 -0
- package/dist/daemon/storage.d.ts.map +1 -0
- package/dist/daemon/storage.js +215 -0
- package/dist/daemon/storage.js.map +1 -0
- package/dist/shared/daemon.d.ts +14 -0
- package/dist/shared/daemon.d.ts.map +1 -0
- package/dist/shared/daemon.js +161 -0
- package/dist/shared/daemon.js.map +1 -0
- package/dist/shared/project.d.ts +61 -0
- package/dist/shared/project.d.ts.map +1 -0
- package/dist/shared/project.js +143 -0
- package/dist/shared/project.js.map +1 -0
- package/dist/shared/types.d.ts +32 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Root TUI component for browsing captured HTTP traffic.
|
|
4
|
+
*/
|
|
5
|
+
import React, { useState, useCallback } from "react";
|
|
6
|
+
import { Box, Text, useInput, useApp, useStdin } from "ink";
|
|
7
|
+
import { useStdoutDimensions } from "./hooks/useStdoutDimensions.js";
|
|
8
|
+
import { useRequests } from "./hooks/useRequests.js";
|
|
9
|
+
import { useExport } from "./hooks/useExport.js";
|
|
10
|
+
import { RequestList } from "./components/RequestList.js";
|
|
11
|
+
import { RequestDetails } from "./components/RequestDetails.js";
|
|
12
|
+
import { StatusBar } from "./components/StatusBar.js";
|
|
13
|
+
export function App({ label }) {
|
|
14
|
+
const { exit } = useApp();
|
|
15
|
+
const { isRawModeSupported } = useStdin();
|
|
16
|
+
const [columns, rows] = useStdoutDimensions();
|
|
17
|
+
const { requests, isLoading, error, refresh } = useRequests({ label });
|
|
18
|
+
const { exportCurl, exportHar } = useExport();
|
|
19
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
20
|
+
const [activePanel, setActivePanel] = useState("list");
|
|
21
|
+
const [statusMessage, setStatusMessage] = useState();
|
|
22
|
+
// Get the currently selected request
|
|
23
|
+
const selectedRequest = requests[selectedIndex];
|
|
24
|
+
// Clear status message after a delay
|
|
25
|
+
const showStatus = useCallback((message) => {
|
|
26
|
+
setStatusMessage(message);
|
|
27
|
+
setTimeout(() => setStatusMessage(undefined), 3000);
|
|
28
|
+
}, []);
|
|
29
|
+
// Handle keyboard input (only when raw mode is supported, i.e. running in a TTY)
|
|
30
|
+
useInput((input, key) => {
|
|
31
|
+
// Navigation
|
|
32
|
+
if (input === "j" || key.downArrow) {
|
|
33
|
+
setSelectedIndex((prev) => Math.min(prev + 1, requests.length - 1));
|
|
34
|
+
}
|
|
35
|
+
else if (input === "k" || key.upArrow) {
|
|
36
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
|
37
|
+
}
|
|
38
|
+
else if (key.tab) {
|
|
39
|
+
setActivePanel((prev) => (prev === "list" ? "details" : "list"));
|
|
40
|
+
}
|
|
41
|
+
// Actions
|
|
42
|
+
else if (input === "q") {
|
|
43
|
+
exit();
|
|
44
|
+
}
|
|
45
|
+
else if (input === "r") {
|
|
46
|
+
void refresh();
|
|
47
|
+
showStatus("Refreshing...");
|
|
48
|
+
}
|
|
49
|
+
else if (input === "c") {
|
|
50
|
+
if (selectedRequest) {
|
|
51
|
+
const result = exportCurl(selectedRequest);
|
|
52
|
+
if (result.success) {
|
|
53
|
+
// In a real implementation, we'd copy to clipboard or show in a modal
|
|
54
|
+
// For now, just indicate success
|
|
55
|
+
showStatus("Curl copied! Check terminal output after exit.");
|
|
56
|
+
// Store for printing after TUI exits
|
|
57
|
+
globalThis["__htpxCurl"] = result.message;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
showStatus(`Error: ${result.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
showStatus("No request selected");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (input === "h") {
|
|
68
|
+
if (requests.length > 0) {
|
|
69
|
+
const result = exportHar(requests);
|
|
70
|
+
showStatus(result.success ? result.message : `Error: ${result.message}`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
showStatus("No requests to export");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}, { isActive: isRawModeSupported === true });
|
|
77
|
+
// Keep selection in bounds when requests change
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (selectedIndex >= requests.length && requests.length > 0) {
|
|
80
|
+
setSelectedIndex(requests.length - 1);
|
|
81
|
+
}
|
|
82
|
+
}, [requests.length, selectedIndex]);
|
|
83
|
+
// Calculate layout
|
|
84
|
+
const listWidth = Math.floor(columns * 0.4);
|
|
85
|
+
const contentHeight = rows - 3; // Leave room for status bar
|
|
86
|
+
// Loading state
|
|
87
|
+
if (isLoading && requests.length === 0) {
|
|
88
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(Text, { children: "Loading..." }) }), _jsx(StatusBar, {})] }));
|
|
89
|
+
}
|
|
90
|
+
// Error state
|
|
91
|
+
if (error) {
|
|
92
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }), _jsx(StatusBar, { message: "Press 'q' to quit, 'r' to retry" })] }));
|
|
93
|
+
}
|
|
94
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "htpx" }), label && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " \u2502 " }), _jsx(Text, { dimColor: true, children: "label: " }), _jsx(Text, { color: "yellow", children: label })] }))] }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [_jsx(RequestList, { requests: requests, selectedIndex: selectedIndex, isActive: activePanel === "list", width: listWidth, height: contentHeight }), _jsx(RequestDetails, { request: selectedRequest, isActive: activePanel === "details", height: contentHeight })] }), _jsx(StatusBar, { message: statusMessage })] }));
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=App.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../../src/cli/tui/App.tsx"],"names":[],"mappings":";AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAQtD,MAAM,UAAU,GAAG,CAAC,EAAE,KAAK,EAAY;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,mBAAmB,EAAE,CAAC;IAE9C,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAAC;IAE9C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAQ,MAAM,CAAC,CAAC;IAC9D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,EAAsB,CAAC;IAEzE,qCAAqC;IACrC,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAEhD,qCAAqC;IACrC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,EAAE;QACjD,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC1B,UAAU,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iFAAiF;IACjF,QAAQ,CACN,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,aAAa;QACb,IAAI,KAAK,KAAK,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACnC,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACxC,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACnB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,CAAC;QAED,UAAU;aACL,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACvB,IAAI,EAAE,CAAC;QACT,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,KAAK,OAAO,EAAE,CAAC;YACf,UAAU,CAAC,eAAe,CAAC,CAAC;QAC9B,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,IAAI,eAAe,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;gBAC3C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,sEAAsE;oBACtE,iCAAiC;oBACjC,UAAU,CAAC,gDAAgD,CAAC,CAAC;oBAC7D,qCAAqC;oBACpC,UAAsC,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;gBACzE,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,UAAU,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,qBAAqB,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;gBACnC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3E,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,uBAAuB,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC,EACD,EAAE,QAAQ,EAAE,kBAAkB,KAAK,IAAI,EAAE,CAC1C,CAAC;IAEF,gDAAgD;IAChD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,aAAa,IAAI,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5D,gBAAgB,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAErC,mBAAmB;IACnB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,4BAA4B;IAE5D,gBAAgB;IAChB,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,MAAM,EAAE,IAAI,aACtC,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAC,QAAQ,EAAC,cAAc,EAAC,QAAQ,YAC3D,KAAC,IAAI,6BAAkB,GACnB,EACN,KAAC,SAAS,KAAG,IACT,CACP,CAAC;IACJ,CAAC;IAED,cAAc;IACd,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,MAAM,EAAE,IAAI,aACtC,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAC,QAAQ,EAAC,cAAc,EAAC,QAAQ,YAC3D,MAAC,IAAI,IAAC,KAAK,EAAC,KAAK,wBAAS,KAAK,IAAQ,GACnC,EACN,KAAC,SAAS,IAAC,OAAO,EAAC,iCAAiC,GAAG,IACnD,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,MAAM,EAAE,IAAI,aAEtC,MAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,aACd,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,qBAEhB,EACN,KAAK,IAAI,CACR,8BACE,KAAC,IAAI,2BAAW,EAChB,KAAC,IAAI,IAAC,QAAQ,8BAAe,EAC7B,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,YAAE,KAAK,GAAQ,IAClC,CACJ,IACG,EAGN,MAAC,GAAG,IAAC,aAAa,EAAC,KAAK,EAAC,QAAQ,EAAE,CAAC,aAClC,KAAC,WAAW,IACV,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,WAAW,KAAK,MAAM,EAChC,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,aAAa,GACrB,EACF,KAAC,cAAc,IACb,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,WAAW,KAAK,SAAS,EACnC,MAAM,EAAE,aAAa,GACrB,IACE,EAGN,KAAC,SAAS,IAAC,OAAO,EAAE,aAAa,GAAI,IACjC,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display HTTP body with JSON pretty-printing.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
interface BodyViewProps {
|
|
6
|
+
title: string;
|
|
7
|
+
body: Buffer | undefined;
|
|
8
|
+
contentType?: string;
|
|
9
|
+
maxLines?: number;
|
|
10
|
+
scrollOffset?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function BodyView({ title, body, contentType, maxLines, scrollOffset, }: BodyViewProps): React.ReactElement;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=BodyView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyView.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/BodyView.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAkB,MAAM,OAAO,CAAC;AAIvC,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAgCD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,YAAgB,GACjB,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CA0BpC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Display HTTP body with JSON pretty-printing.
|
|
4
|
+
*/
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import { Box, Text } from "ink";
|
|
7
|
+
import { formatSize } from "../utils/formatters.js";
|
|
8
|
+
/**
|
|
9
|
+
* Attempt to parse and format JSON body.
|
|
10
|
+
*/
|
|
11
|
+
function formatBody(body, contentType) {
|
|
12
|
+
if (!body || body.length === 0) {
|
|
13
|
+
return ["(empty)"];
|
|
14
|
+
}
|
|
15
|
+
const bodyStr = body.toString("utf-8");
|
|
16
|
+
// Try JSON formatting if content type suggests JSON or body looks like JSON
|
|
17
|
+
const isJson = contentType?.includes("application/json") ||
|
|
18
|
+
bodyStr.trimStart().startsWith("{") ||
|
|
19
|
+
bodyStr.trimStart().startsWith("[");
|
|
20
|
+
if (isJson) {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(bodyStr);
|
|
23
|
+
const formatted = JSON.stringify(parsed, null, 2);
|
|
24
|
+
return formatted.split("\n");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Not valid JSON, show as-is
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// For non-JSON or invalid JSON, show as-is with line breaks
|
|
31
|
+
return bodyStr.split("\n");
|
|
32
|
+
}
|
|
33
|
+
export function BodyView({ title, body, contentType, maxLines, scrollOffset = 0, }) {
|
|
34
|
+
const lines = useMemo(() => formatBody(body, contentType), [body, contentType]);
|
|
35
|
+
const visibleLines = maxLines !== undefined ? lines.slice(scrollOffset, scrollOffset + maxLines) : lines;
|
|
36
|
+
const sizeStr = body ? formatSize(body.length) : "-";
|
|
37
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "yellow", children: title }), _jsxs(Text, { dimColor: true, children: [" (", sizeStr, ")"] })] }), visibleLines.map((line, index) => (_jsx(Text, { wrap: "truncate", children: line }, scrollOffset + index))), maxLines !== undefined && lines.length > maxLines + scrollOffset && (_jsxs(Text, { dimColor: true, children: ["... ", lines.length - maxLines - scrollOffset, " more lines"] }))] }));
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=BodyView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyView.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/BodyView.tsx"],"names":[],"mappings":";AAAA;;GAEG;AAEH,OAAc,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAUpD;;GAEG;AACH,SAAS,UAAU,CAAC,IAAwB,EAAE,WAAoB;IAChE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,SAAS,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEvC,4EAA4E;IAC5E,MAAM,MAAM,GACV,WAAW,EAAE,QAAQ,CAAC,kBAAkB,CAAC;QACzC,OAAO,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QACnC,OAAO,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAEtC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAClD,OAAO,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,YAAY,GAAG,CAAC,GACF;IACd,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAEhF,MAAM,YAAY,GAChB,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEtF,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAErD,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,QAAQ,YACtB,KAAK,GACD,EACP,MAAC,IAAI,IAAC,QAAQ,yBAAI,OAAO,SAAS,IAC9B,EACL,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CACjC,KAAC,IAAI,IAA4B,IAAI,EAAC,UAAU,YAC7C,IAAI,IADI,YAAY,GAAG,KAAK,CAExB,CACR,CAAC,EACD,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,YAAY,IAAI,CACnE,MAAC,IAAI,IAAC,QAAQ,2BAAM,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,YAAY,mBAAmB,CAC9E,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display HTTP headers in a formatted view.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
interface HeadersViewProps {
|
|
6
|
+
title: string;
|
|
7
|
+
headers: Record<string, string> | undefined;
|
|
8
|
+
maxLines?: number;
|
|
9
|
+
scrollOffset?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function HeadersView({ title, headers, maxLines, scrollOffset, }: HeadersViewProps): React.ReactElement;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=HeadersView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeadersView.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/HeadersView.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,YAAgB,GACjB,EAAE,gBAAgB,GAAG,KAAK,CAAC,YAAY,CA2BvC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
export function HeadersView({ title, headers, maxLines, scrollOffset = 0, }) {
|
|
4
|
+
const entries = headers ? Object.entries(headers) : [];
|
|
5
|
+
const visibleEntries = maxLines !== undefined ? entries.slice(scrollOffset, scrollOffset + maxLines) : entries;
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "yellow", children: title }), entries.length === 0 ? (_jsx(Text, { dimColor: true, children: "No headers" })) : (visibleEntries.map(([name, value]) => (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: name }), _jsx(Text, { children: ": " }), _jsx(Text, { children: value })] }, name)))), maxLines !== undefined && entries.length > maxLines && (_jsxs(Text, { dimColor: true, children: ["... and ", entries.length - maxLines, " more"] }))] }));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=HeadersView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeadersView.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/HeadersView.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAShC,MAAM,UAAU,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,YAAY,GAAG,CAAC,GACC;IACjB,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvD,MAAM,cAAc,GAClB,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAE1F,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,QAAQ,YACtB,KAAK,GACD,EACN,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACtB,KAAC,IAAI,IAAC,QAAQ,iCAAkB,CACjC,CAAC,CAAC,CAAC,CACF,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CACpC,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,YAAE,IAAI,GAAQ,EAChC,KAAC,IAAI,qBAAU,EACf,KAAC,IAAI,cAAE,KAAK,GAAQ,KAHZ,IAAI,CAIR,CACP,CAAC,CACH,EACA,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,QAAQ,IAAI,CACtD,MAAC,IAAI,IAAC,QAAQ,+BAAU,OAAO,CAAC,MAAM,GAAG,QAAQ,aAAa,CAC/D,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Right panel: detailed view of the selected request.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
6
|
+
interface RequestDetailsProps {
|
|
7
|
+
request: CapturedRequest | undefined;
|
|
8
|
+
isActive: boolean;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function RequestDetails({ request, isActive, height, }: RequestDetailsProps): React.ReactElement;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=RequestDetails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestDetails.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestDetails.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKhE,UAAU,mBAAmB;IAC3B,OAAO,EAAE,eAAe,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,QAAQ,EACR,MAAM,GACP,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CA2G1C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { HeadersView } from "./HeadersView.js";
|
|
4
|
+
import { BodyView } from "./BodyView.js";
|
|
5
|
+
import { formatRelativeTime, formatDuration } from "../utils/formatters.js";
|
|
6
|
+
export function RequestDetails({ request, isActive, height, }) {
|
|
7
|
+
const borderColour = isActive ? "cyan" : "gray";
|
|
8
|
+
if (!request) {
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, height: height, borderStyle: "single", borderColor: borderColour, paddingX: 1, children: [_jsx(Text, { bold: true, color: isActive ? "cyan" : "white", children: "Details" }), _jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "Select a request to view details" }) })] }));
|
|
10
|
+
}
|
|
11
|
+
// Calculate how much space we have for each section
|
|
12
|
+
const headerHeight = 6; // URL line + timing + separators
|
|
13
|
+
const availableHeight = Math.max(8, height - headerHeight);
|
|
14
|
+
const sectionHeight = Math.floor(availableHeight / 4);
|
|
15
|
+
const reqContentType = request.requestHeaders["content-type"];
|
|
16
|
+
const resContentType = request.responseHeaders?.["content-type"];
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, height: height, borderStyle: "single", borderColor: borderColour, paddingX: 1, overflowY: "hidden", children: [_jsx(Text, { bold: true, color: isActive ? "cyan" : "white", children: "Details" }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "green", bold: true, children: request.method }), _jsx(Text, { children: " " }), _jsx(Text, { children: request.url })] }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "Status: " }), _jsx(Text, { color: request.responseStatus && request.responseStatus < 400 ? "green" : "red", children: request.responseStatus ?? "pending" }), _jsx(Text, { dimColor: true, children: " \u2502 Duration: " }), _jsx(Text, { children: formatDuration(request.durationMs) }), _jsx(Text, { dimColor: true, children: " \u2502 " }), _jsx(Text, { dimColor: true, children: formatRelativeTime(request.timestamp) })] }), _jsx(Box, { marginBottom: 1, children: _jsx(HeadersView, { title: "Request Headers", headers: request.requestHeaders, maxLines: sectionHeight }) }), request.requestBody && request.requestBody.length > 0 && (_jsx(Box, { marginBottom: 1, children: _jsx(BodyView, { title: "Request Body", body: request.requestBody, contentType: reqContentType, maxLines: sectionHeight }) })), request.responseHeaders && (_jsx(Box, { marginBottom: 1, children: _jsx(HeadersView, { title: "Response Headers", headers: request.responseHeaders, maxLines: sectionHeight }) })), request.responseBody && request.responseBody.length > 0 && (_jsx(Box, { marginBottom: 1, children: _jsx(BodyView, { title: "Response Body", body: request.responseBody, contentType: resContentType, maxLines: sectionHeight }) }))] }));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=RequestDetails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestDetails.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestDetails.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAQ5E,MAAM,UAAU,cAAc,CAAC,EAC7B,OAAO,EACP,QAAQ,EACR,MAAM,GACc;IACpB,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAEhD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,QAAQ,EAAE,CAAC,EACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,CAAC,aAEX,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,wBAEtC,EACP,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAC,QAAQ,EAAC,cAAc,EAAC,QAAQ,YAC3D,KAAC,IAAI,IAAC,QAAQ,uDAAwC,GAClD,IACF,CACP,CAAC;IACJ,CAAC;IAED,oDAAoD;IACpD,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,iCAAiC;IACzD,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC;IAC3D,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;IAEtD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,cAAc,CAAC,CAAC;IAEjE,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,QAAQ,EAAE,CAAC,EACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,CAAC,EACX,SAAS,EAAC,QAAQ,aAElB,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,wBAEtC,EAGP,MAAC,GAAG,IAAC,YAAY,EAAE,CAAC,aAClB,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,kBACrB,OAAO,CAAC,MAAM,GACV,EACP,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,cAAE,OAAO,CAAC,GAAG,GAAQ,IACtB,EAGN,MAAC,GAAG,IAAC,YAAY,EAAE,CAAC,aAClB,KAAC,IAAI,IAAC,QAAQ,+BAAgB,EAC9B,KAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,YAClF,OAAO,CAAC,cAAc,IAAI,SAAS,GAC/B,EACP,KAAC,IAAI,IAAC,QAAQ,yCAAqB,EACnC,KAAC,IAAI,cAAE,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,GAAQ,EACjD,KAAC,IAAI,IAAC,QAAQ,+BAAW,EACzB,KAAC,IAAI,IAAC,QAAQ,kBAAE,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,GAAQ,IACzD,EAGN,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YAClB,KAAC,WAAW,IAAC,KAAK,EAAC,iBAAiB,EAAC,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,QAAQ,EAAE,aAAa,GAAI,GAC7F,EAGL,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CACxD,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YAClB,KAAC,QAAQ,IACP,KAAK,EAAC,cAAc,EACpB,IAAI,EAAE,OAAO,CAAC,WAAW,EACzB,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,aAAa,GACvB,GACE,CACP,EAGA,OAAO,CAAC,eAAe,IAAI,CAC1B,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YAClB,KAAC,WAAW,IACV,KAAK,EAAC,kBAAkB,EACxB,OAAO,EAAE,OAAO,CAAC,eAAe,EAChC,QAAQ,EAAE,aAAa,GACvB,GACE,CACP,EAGA,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAC1D,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YAClB,KAAC,QAAQ,IACP,KAAK,EAAC,eAAe,EACrB,IAAI,EAAE,OAAO,CAAC,YAAY,EAC1B,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,aAAa,GACvB,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Left panel: scrollable list of captured requests.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
6
|
+
interface RequestListProps {
|
|
7
|
+
requests: CapturedRequest[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
isActive: boolean;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function RequestList({ requests, selectedIndex, isActive, width, height, }: RequestListProps): React.ReactElement;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=RequestList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestList.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestList.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGhE,UAAU,gBAAgB;IACxB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,KAAK,EACL,MAAM,GACP,EAAE,gBAAgB,GAAG,KAAK,CAAC,YAAY,CAqDvC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { RequestListItem } from "./RequestListItem.js";
|
|
4
|
+
export function RequestList({ requests, selectedIndex, isActive, width, height, }) {
|
|
5
|
+
// Calculate visible window (accounting for border and header)
|
|
6
|
+
const visibleHeight = Math.max(1, height - 3); // Border + header row
|
|
7
|
+
const halfWindow = Math.floor(visibleHeight / 2);
|
|
8
|
+
// Calculate scroll offset to keep selection centered
|
|
9
|
+
let scrollOffset = 0;
|
|
10
|
+
if (requests.length > visibleHeight) {
|
|
11
|
+
scrollOffset = Math.max(0, Math.min(selectedIndex - halfWindow, requests.length - visibleHeight));
|
|
12
|
+
}
|
|
13
|
+
const visibleRequests = requests.slice(scrollOffset, scrollOffset + visibleHeight);
|
|
14
|
+
const borderColour = isActive ? "cyan" : "gray";
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", width: width, height: height, borderStyle: "single", borderColor: borderColour, children: [_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { bold: true, color: isActive ? "cyan" : "white", children: ["Requests (", requests.length, ")"] }), requests.length > visibleHeight && (_jsxs(Text, { dimColor: true, children: [" ", "[", scrollOffset + 1, "-", Math.min(scrollOffset + visibleHeight, requests.length), "]"] }))] }), requests.length === 0 ? (_jsx(Box, { paddingX: 1, paddingY: 1, children: _jsx(Text, { dimColor: true, children: "No requests captured yet" }) })) : (_jsx(Box, { flexDirection: "column", paddingX: 1, children: visibleRequests.map((request, index) => (_jsx(RequestListItem, { request: request, isSelected: scrollOffset + index === selectedIndex, width: width - 4 }, request.id))) }))] }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=RequestList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestList.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestList.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAUvD,MAAM,UAAU,WAAW,CAAC,EAC1B,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,KAAK,EACL,MAAM,GACW;IACjB,8DAA8D;IAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB;IACrE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IAEjD,qDAAqD;IACrD,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,QAAQ,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QACpC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,UAAU,EAAE,QAAQ,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC;IACpG,CAAC;IAED,MAAM,eAAe,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,aAAa,CAAC,CAAC;IAEnF,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAEhD,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,YAAY,aAEzB,MAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,aACd,MAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,2BAChC,QAAQ,CAAC,MAAM,SACrB,EACN,QAAQ,CAAC,MAAM,GAAG,aAAa,IAAI,CAClC,MAAC,IAAI,IAAC,QAAQ,mBACX,GAAG,OACF,YAAY,GAAG,CAAC,OAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,SACvE,CACR,IACG,EAEL,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACvB,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,YAC3B,KAAC,IAAI,IAAC,QAAQ,+CAAgC,GAC1C,CACP,CAAC,CAAC,CAAC,CACF,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,YACpC,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CACvC,KAAC,eAAe,IAEd,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,YAAY,GAAG,KAAK,KAAK,aAAa,EAClD,KAAK,EAAE,KAAK,GAAG,CAAC,IAHX,OAAO,CAAC,EAAE,CAIf,CACH,CAAC,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single request row in the request list.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
6
|
+
interface RequestListItemProps {
|
|
7
|
+
request: CapturedRequest;
|
|
8
|
+
isSelected: boolean;
|
|
9
|
+
width: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function RequestListItem({ request, isSelected, width, }: RequestListItemProps): React.ReactElement;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=RequestListItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestListItem.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestListItem.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGhE,UAAU,oBAAoB;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAyCD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,UAAU,EACV,KAAK,GACN,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CA0B3C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { formatMethod, formatDuration, truncate } from "../utils/formatters.js";
|
|
4
|
+
/**
|
|
5
|
+
* Get colour for HTTP status code.
|
|
6
|
+
*/
|
|
7
|
+
function getStatusColour(status) {
|
|
8
|
+
if (status === undefined) {
|
|
9
|
+
return "gray";
|
|
10
|
+
}
|
|
11
|
+
if (status >= 200 && status < 300) {
|
|
12
|
+
return "green";
|
|
13
|
+
}
|
|
14
|
+
if (status >= 300 && status < 400) {
|
|
15
|
+
return "yellow";
|
|
16
|
+
}
|
|
17
|
+
if (status >= 400) {
|
|
18
|
+
return "red";
|
|
19
|
+
}
|
|
20
|
+
return "white";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get colour for HTTP method.
|
|
24
|
+
*/
|
|
25
|
+
function getMethodColour(method) {
|
|
26
|
+
switch (method.toUpperCase()) {
|
|
27
|
+
case "GET":
|
|
28
|
+
return "green";
|
|
29
|
+
case "POST":
|
|
30
|
+
return "blue";
|
|
31
|
+
case "PUT":
|
|
32
|
+
return "yellow";
|
|
33
|
+
case "PATCH":
|
|
34
|
+
return "yellow";
|
|
35
|
+
case "DELETE":
|
|
36
|
+
return "red";
|
|
37
|
+
default:
|
|
38
|
+
return "white";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function RequestListItem({ request, isSelected, width, }) {
|
|
42
|
+
const methodWidth = 7;
|
|
43
|
+
const statusWidth = 4;
|
|
44
|
+
const durationWidth = 8;
|
|
45
|
+
const separatorsWidth = 3; // Spaces between columns
|
|
46
|
+
// Calculate remaining width for path
|
|
47
|
+
const pathWidth = Math.max(10, width - methodWidth - statusWidth - durationWidth - separatorsWidth);
|
|
48
|
+
const displayPath = truncate(request.path, pathWidth);
|
|
49
|
+
const statusText = request.responseStatus?.toString() ?? "...";
|
|
50
|
+
const duration = formatDuration(request.durationMs);
|
|
51
|
+
return (_jsxs(Box, { children: [isSelected && _jsx(Text, { color: "cyan", children: "\u276F " }), !isSelected && _jsx(Text, { children: " " }), _jsx(Text, { color: getMethodColour(request.method), children: formatMethod(request.method) }), _jsx(Text, { children: " " }), _jsx(Text, { color: getStatusColour(request.responseStatus), children: statusText.padStart(3) }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: !isSelected, children: displayPath }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, children: duration.padStart(durationWidth) })] }));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=RequestListItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestListItem.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/RequestListItem.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAQhF;;GAEG;AACH,SAAS,eAAe,CAAC,MAA0B;IACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,QAAQ,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,OAAO,CAAC;QACjB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,KAAK;YACR,OAAO,QAAQ,CAAC;QAClB,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,UAAU,EACV,KAAK,GACgB;IACrB,MAAM,WAAW,GAAG,CAAC,CAAC;IACtB,MAAM,WAAW,GAAG,CAAC,CAAC;IACtB,MAAM,aAAa,GAAG,CAAC,CAAC;IACxB,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,yBAAyB;IAEpD,qCAAqC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,eAAe,CAAC,CAAC;IACpG,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEtD,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,KAAK,CAAC;IAC/D,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEpD,OAAO,CACL,MAAC,GAAG,eACD,UAAU,IAAI,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,wBAAU,EAC1C,CAAC,UAAU,IAAI,KAAC,IAAI,qBAAU,EAC/B,KAAC,IAAI,IAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,YAAG,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,GAAQ,EACnF,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,IAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,cAAc,CAAC,YAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAQ,EACrF,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,IAAC,QAAQ,EAAE,CAAC,UAAU,YAAG,WAAW,GAAQ,EACjD,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,GAAI,EACpB,KAAC,IAAI,IAAC,QAAQ,kBAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAQ,IACpD,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status bar showing keybinding hints at the bottom of the TUI.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
interface StatusBarProps {
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function StatusBar({ message }: StatusBarProps): React.ReactElement;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=StatusBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CA2BzE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Status bar showing keybinding hints at the bottom of the TUI.
|
|
4
|
+
*/
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { Box, Text } from "ink";
|
|
7
|
+
const KEY_HINTS = [
|
|
8
|
+
{ key: "j/k", action: "navigate" },
|
|
9
|
+
{ key: "Tab", action: "switch panel" },
|
|
10
|
+
{ key: "c", action: "curl" },
|
|
11
|
+
{ key: "h", action: "HAR" },
|
|
12
|
+
{ key: "r", action: "refresh" },
|
|
13
|
+
{ key: "q", action: "quit" },
|
|
14
|
+
];
|
|
15
|
+
export function StatusBar({ message }) {
|
|
16
|
+
return (_jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, paddingX: 1, children: _jsx(Box, { flexGrow: 1, children: message ? (_jsx(Text, { color: "yellow", children: message })) : (KEY_HINTS.map((hint, index) => (_jsxs(React.Fragment, { children: [_jsx(Text, { color: "cyan", bold: true, children: hint.key }), _jsxs(Text, { dimColor: true, children: [" ", hint.action] }), index < KEY_HINTS.length - 1 && _jsx(Text, { children: " \u2502 " })] }, hint.key)))) }) }));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/StatusBar.tsx"],"names":[],"mappings":";AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAOhC,MAAM,SAAS,GAAc;IAC3B,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE;IAClC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE;IACtC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE;IAC5B,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE;IAC3B,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;IAC/B,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE;CAC7B,CAAC;AAMF,MAAM,UAAU,SAAS,CAAC,EAAE,OAAO,EAAkB;IACnD,OAAO,CACL,KAAC,GAAG,IACF,WAAW,EAAC,QAAQ,EACpB,SAAS,QACT,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,KAAK,EACjB,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,CAAC,YAEX,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,YACb,OAAO,CAAC,CAAC,CAAC,CACT,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,YAAE,OAAO,GAAQ,CACtC,CAAC,CAAC,CAAC,CACF,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC7B,MAAC,KAAK,CAAC,QAAQ,eACb,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBACpB,IAAI,CAAC,GAAG,GACJ,EACP,MAAC,IAAI,IAAC,QAAQ,wBAAG,IAAI,CAAC,MAAM,IAAQ,EACnC,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,KAAC,IAAI,2BAAW,KAL9B,IAAI,CAAC,GAAG,CAMZ,CAClB,CAAC,CACH,GACG,GACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for exporting captured requests to various formats.
|
|
3
|
+
*/
|
|
4
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
5
|
+
interface ExportResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
interface UseExportResult {
|
|
10
|
+
exportCurl: (request: CapturedRequest) => ExportResult;
|
|
11
|
+
exportHar: (requests: CapturedRequest[], filename?: string) => ExportResult;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Hook providing export functionality for captured requests.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useExport(): UseExportResult;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=useExport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useExport.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useExport.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE,UAAU,YAAY;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,eAAe;IACvB,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,YAAY,CAAC;IACvD,SAAS,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,YAAY,CAAC;CAC7E;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAkD3C"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for exporting captured requests to various formats.
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback } from "react";
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { generateCurl } from "../utils/curl.js";
|
|
8
|
+
import { generateHarString } from "../utils/har.js";
|
|
9
|
+
/**
|
|
10
|
+
* Hook providing export functionality for captured requests.
|
|
11
|
+
*/
|
|
12
|
+
export function useExport() {
|
|
13
|
+
/**
|
|
14
|
+
* Generate curl command for a request.
|
|
15
|
+
* Returns the curl string to be displayed/copied.
|
|
16
|
+
*/
|
|
17
|
+
const exportCurl = useCallback((request) => {
|
|
18
|
+
try {
|
|
19
|
+
const curl = generateCurl(request);
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
message: curl,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return {
|
|
27
|
+
success: false,
|
|
28
|
+
message: err instanceof Error ? err.message : "Failed to generate curl",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}, []);
|
|
32
|
+
/**
|
|
33
|
+
* Export requests to HAR file.
|
|
34
|
+
*/
|
|
35
|
+
const exportHar = useCallback((requests, filename) => {
|
|
36
|
+
try {
|
|
37
|
+
const harFilename = filename ?? `htpx-export-${Date.now()}.har`;
|
|
38
|
+
const harPath = path.resolve(process.cwd(), harFilename);
|
|
39
|
+
const harContent = generateHarString(requests);
|
|
40
|
+
fs.writeFileSync(harPath, harContent, "utf-8");
|
|
41
|
+
return {
|
|
42
|
+
success: true,
|
|
43
|
+
message: `Exported ${requests.length} request(s) to ${harPath}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
message: err instanceof Error ? err.message : "Failed to export HAR",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}, []);
|
|
53
|
+
return {
|
|
54
|
+
exportCurl,
|
|
55
|
+
exportHar,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=useExport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useExport.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useExport.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAYpD;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB;;;OAGG;IACH,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,OAAwB,EAAgB,EAAE;QACxE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB;aACxE,CAAC;QACJ,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP;;OAEG;IACH,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,QAA2B,EAAE,QAAiB,EAAgB,EAAE;QAC/D,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,QAAQ,IAAI,eAAe,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;YAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAE/C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;YAE/C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,YAAY,QAAQ,CAAC,MAAM,kBAAkB,OAAO,EAAE;aAChE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB;aACrE,CAAC;QACJ,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,OAAO;QACL,UAAU;QACV,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and polling captured requests from the daemon.
|
|
3
|
+
*/
|
|
4
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
5
|
+
interface UseRequestsOptions {
|
|
6
|
+
label?: string;
|
|
7
|
+
pollInterval?: number;
|
|
8
|
+
}
|
|
9
|
+
interface UseRequestsResult {
|
|
10
|
+
requests: CapturedRequest[];
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
error: string | null;
|
|
13
|
+
refresh: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Hook to fetch and poll for captured requests.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useRequests(options?: UseRequestsOptions): UseRequestsResult;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=useRequests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRequests.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useRequests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE,UAAU,kBAAkB;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,iBAAiB;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAmF/E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and polling captured requests from the daemon.
|
|
3
|
+
*/
|
|
4
|
+
import { useState, useEffect, useCallback, useRef } from "react";
|
|
5
|
+
import { ControlClient } from "../../../daemon/control.js";
|
|
6
|
+
import { findProjectRoot, getHtpxPaths } from "../../../shared/project.js";
|
|
7
|
+
/**
|
|
8
|
+
* Hook to fetch and poll for captured requests.
|
|
9
|
+
*/
|
|
10
|
+
export function useRequests(options = {}) {
|
|
11
|
+
const { label, pollInterval = 2000 } = options;
|
|
12
|
+
const [requests, setRequests] = useState([]);
|
|
13
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const clientRef = useRef(null);
|
|
16
|
+
const lastCountRef = useRef(0);
|
|
17
|
+
// Initialise control client
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const projectRoot = findProjectRoot();
|
|
20
|
+
if (!projectRoot) {
|
|
21
|
+
setError("Not in an htpx project. Run 'htpx init' first.");
|
|
22
|
+
setIsLoading(false);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const paths = getHtpxPaths(projectRoot);
|
|
26
|
+
clientRef.current = new ControlClient(paths.controlSocketFile);
|
|
27
|
+
}, []);
|
|
28
|
+
// Fetch requests from daemon
|
|
29
|
+
const fetchRequests = useCallback(async () => {
|
|
30
|
+
const client = clientRef.current;
|
|
31
|
+
if (!client) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
// First check the count to avoid unnecessary data transfer
|
|
36
|
+
const count = await client.countRequests({ label });
|
|
37
|
+
// Only fetch full list if count changed
|
|
38
|
+
if (count !== lastCountRef.current || requests.length === 0) {
|
|
39
|
+
const newRequests = await client.listRequests({
|
|
40
|
+
label,
|
|
41
|
+
limit: 1000,
|
|
42
|
+
});
|
|
43
|
+
setRequests(newRequests);
|
|
44
|
+
lastCountRef.current = count;
|
|
45
|
+
}
|
|
46
|
+
setError(null);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const message = err instanceof Error ? err.message : "Failed to connect to daemon";
|
|
50
|
+
if (message.includes("ENOENT") || message.includes("ECONNREFUSED")) {
|
|
51
|
+
setError("Daemon not running. Start with 'htpx intercept'.");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
setError(message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setIsLoading(false);
|
|
59
|
+
}
|
|
60
|
+
}, [label, requests.length]);
|
|
61
|
+
// Manual refresh function
|
|
62
|
+
const refresh = useCallback(async () => {
|
|
63
|
+
setIsLoading(true);
|
|
64
|
+
lastCountRef.current = 0; // Force full refresh
|
|
65
|
+
await fetchRequests();
|
|
66
|
+
}, [fetchRequests]);
|
|
67
|
+
// Initial fetch
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
void fetchRequests();
|
|
70
|
+
}, [fetchRequests]);
|
|
71
|
+
// Polling
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const interval = setInterval(() => {
|
|
74
|
+
void fetchRequests();
|
|
75
|
+
}, pollInterval);
|
|
76
|
+
return () => clearInterval(interval);
|
|
77
|
+
}, [fetchRequests, pollInterval]);
|
|
78
|
+
return {
|
|
79
|
+
requests,
|
|
80
|
+
isLoading,
|
|
81
|
+
error,
|
|
82
|
+
refresh,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=useRequests.js.map
|