htpx-cli 0.1.2 → 0.2.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 (252) hide show
  1. package/LICENSE +665 -21
  2. package/README.md +422 -116
  3. package/dist/cli/commands/clear.d.ts.map +1 -1
  4. package/dist/cli/commands/clear.js +11 -11
  5. package/dist/cli/commands/clear.js.map +1 -1
  6. package/dist/cli/commands/daemon.d.ts +3 -0
  7. package/dist/cli/commands/daemon.d.ts.map +1 -0
  8. package/dist/cli/commands/daemon.js +59 -0
  9. package/dist/cli/commands/daemon.js.map +1 -0
  10. package/dist/cli/commands/debug-dump.d.ts.map +1 -1
  11. package/dist/cli/commands/debug-dump.js +8 -10
  12. package/dist/cli/commands/debug-dump.js.map +1 -1
  13. package/dist/cli/commands/helpers.d.ts +18 -0
  14. package/dist/cli/commands/helpers.d.ts.map +1 -0
  15. package/dist/cli/commands/helpers.js +34 -0
  16. package/dist/cli/commands/helpers.js.map +1 -0
  17. package/dist/cli/commands/init.d.ts +1 -1
  18. package/dist/cli/commands/init.d.ts.map +1 -1
  19. package/dist/cli/commands/init.js +3 -4
  20. package/dist/cli/commands/init.js.map +1 -1
  21. package/dist/cli/commands/intercept.d.ts +2 -1
  22. package/dist/cli/commands/intercept.d.ts.map +1 -1
  23. package/dist/cli/commands/intercept.js +74 -30
  24. package/dist/cli/commands/intercept.js.map +1 -1
  25. package/dist/cli/commands/interceptors.d.ts +3 -0
  26. package/dist/cli/commands/interceptors.d.ts.map +1 -0
  27. package/dist/cli/commands/interceptors.js +163 -0
  28. package/dist/cli/commands/interceptors.js.map +1 -0
  29. package/dist/cli/commands/mcp.d.ts +3 -0
  30. package/dist/cli/commands/mcp.d.ts.map +1 -0
  31. package/dist/cli/commands/mcp.js +24 -0
  32. package/dist/cli/commands/mcp.js.map +1 -0
  33. package/dist/cli/commands/off.d.ts +8 -0
  34. package/dist/cli/commands/off.d.ts.map +1 -0
  35. package/dist/cli/commands/off.js +34 -0
  36. package/dist/cli/commands/off.js.map +1 -0
  37. package/dist/cli/commands/on.d.ts +9 -0
  38. package/dist/cli/commands/on.d.ts.map +1 -0
  39. package/dist/cli/commands/on.js +121 -0
  40. package/dist/cli/commands/on.js.map +1 -0
  41. package/dist/cli/commands/project.d.ts.map +1 -1
  42. package/dist/cli/commands/project.js +5 -3
  43. package/dist/cli/commands/project.js.map +1 -1
  44. package/dist/cli/commands/restart.d.ts.map +1 -1
  45. package/dist/cli/commands/restart.js +5 -10
  46. package/dist/cli/commands/restart.js.map +1 -1
  47. package/dist/cli/commands/status.d.ts.map +1 -1
  48. package/dist/cli/commands/status.js +50 -20
  49. package/dist/cli/commands/status.js.map +1 -1
  50. package/dist/cli/commands/stop.d.ts.map +1 -1
  51. package/dist/cli/commands/stop.js +7 -10
  52. package/dist/cli/commands/stop.js.map +1 -1
  53. package/dist/cli/commands/tui.d.ts.map +1 -1
  54. package/dist/cli/commands/tui.js +6 -13
  55. package/dist/cli/commands/tui.js.map +1 -1
  56. package/dist/cli/index.js +12 -7
  57. package/dist/cli/index.js.map +1 -1
  58. package/dist/cli/tui/App.d.ts +7 -2
  59. package/dist/cli/tui/App.d.ts.map +1 -1
  60. package/dist/cli/tui/App.js +490 -33
  61. package/dist/cli/tui/App.js.map +1 -1
  62. package/dist/cli/tui/components/AccordionContent.d.ts +28 -0
  63. package/dist/cli/tui/components/AccordionContent.d.ts.map +1 -0
  64. package/dist/cli/tui/components/AccordionContent.js +87 -0
  65. package/dist/cli/tui/components/AccordionContent.js.map +1 -0
  66. package/dist/cli/tui/components/AccordionPanel.d.ts +38 -0
  67. package/dist/cli/tui/components/AccordionPanel.d.ts.map +1 -0
  68. package/dist/cli/tui/components/AccordionPanel.js +110 -0
  69. package/dist/cli/tui/components/AccordionPanel.js.map +1 -0
  70. package/dist/cli/tui/components/AccordionSection.d.ts +32 -0
  71. package/dist/cli/tui/components/AccordionSection.d.ts.map +1 -0
  72. package/dist/cli/tui/components/AccordionSection.js +41 -0
  73. package/dist/cli/tui/components/AccordionSection.js.map +1 -0
  74. package/dist/cli/tui/components/ExportModal.d.ts +34 -0
  75. package/dist/cli/tui/components/ExportModal.d.ts.map +1 -0
  76. package/dist/cli/tui/components/ExportModal.js +109 -0
  77. package/dist/cli/tui/components/ExportModal.js.map +1 -0
  78. package/dist/cli/tui/components/FilterBar.d.ts +21 -0
  79. package/dist/cli/tui/components/FilterBar.d.ts.map +1 -0
  80. package/dist/cli/tui/components/FilterBar.js +155 -0
  81. package/dist/cli/tui/components/FilterBar.js.map +1 -0
  82. package/dist/cli/tui/components/HelpModal.d.ts +13 -0
  83. package/dist/cli/tui/components/HelpModal.d.ts.map +1 -0
  84. package/dist/cli/tui/components/HelpModal.js +78 -0
  85. package/dist/cli/tui/components/HelpModal.js.map +1 -0
  86. package/dist/cli/tui/components/HintContent.d.ts +25 -0
  87. package/dist/cli/tui/components/HintContent.d.ts.map +1 -0
  88. package/dist/cli/tui/components/HintContent.js +44 -0
  89. package/dist/cli/tui/components/HintContent.js.map +1 -0
  90. package/dist/cli/tui/components/InfoModal.d.ts +15 -0
  91. package/dist/cli/tui/components/InfoModal.d.ts.map +1 -0
  92. package/dist/cli/tui/components/InfoModal.js +17 -0
  93. package/dist/cli/tui/components/InfoModal.js.map +1 -0
  94. package/dist/cli/tui/components/JsonExplorerModal.d.ts +24 -0
  95. package/dist/cli/tui/components/JsonExplorerModal.d.ts.map +1 -0
  96. package/dist/cli/tui/components/JsonExplorerModal.js +311 -0
  97. package/dist/cli/tui/components/JsonExplorerModal.js.map +1 -0
  98. package/dist/cli/tui/components/Modal.d.ts +26 -0
  99. package/dist/cli/tui/components/Modal.d.ts.map +1 -0
  100. package/dist/cli/tui/components/Modal.js +15 -0
  101. package/dist/cli/tui/components/Modal.js.map +1 -0
  102. package/dist/cli/tui/components/Panel.d.ts +19 -0
  103. package/dist/cli/tui/components/Panel.d.ts.map +1 -0
  104. package/dist/cli/tui/components/Panel.js +37 -0
  105. package/dist/cli/tui/components/Panel.js.map +1 -0
  106. package/dist/cli/tui/components/RequestDetails.d.ts +4 -1
  107. package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -1
  108. package/dist/cli/tui/components/RequestDetails.js +9 -5
  109. package/dist/cli/tui/components/RequestDetails.js.map +1 -1
  110. package/dist/cli/tui/components/RequestList.d.ts +9 -3
  111. package/dist/cli/tui/components/RequestList.d.ts.map +1 -1
  112. package/dist/cli/tui/components/RequestList.js +24 -11
  113. package/dist/cli/tui/components/RequestList.js.map +1 -1
  114. package/dist/cli/tui/components/RequestListItem.d.ts +26 -3
  115. package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -1
  116. package/dist/cli/tui/components/RequestListItem.js +86 -9
  117. package/dist/cli/tui/components/RequestListItem.js.map +1 -1
  118. package/dist/cli/tui/components/SaveModal.d.ts +30 -0
  119. package/dist/cli/tui/components/SaveModal.d.ts.map +1 -0
  120. package/dist/cli/tui/components/SaveModal.js +95 -0
  121. package/dist/cli/tui/components/SaveModal.js.map +1 -0
  122. package/dist/cli/tui/components/StatusBar.d.ts +31 -2
  123. package/dist/cli/tui/components/StatusBar.d.ts.map +1 -1
  124. package/dist/cli/tui/components/StatusBar.js +44 -9
  125. package/dist/cli/tui/components/StatusBar.js.map +1 -1
  126. package/dist/cli/tui/components/TextViewerModal.d.ts +19 -0
  127. package/dist/cli/tui/components/TextViewerModal.d.ts.map +1 -0
  128. package/dist/cli/tui/components/TextViewerModal.js +227 -0
  129. package/dist/cli/tui/components/TextViewerModal.js.map +1 -0
  130. package/dist/cli/tui/hooks/useBodyExport.d.ts +26 -0
  131. package/dist/cli/tui/hooks/useBodyExport.d.ts.map +1 -0
  132. package/dist/cli/tui/hooks/useBodyExport.js +173 -0
  133. package/dist/cli/tui/hooks/useBodyExport.js.map +1 -0
  134. package/dist/cli/tui/hooks/useExport.d.ts +13 -2
  135. package/dist/cli/tui/hooks/useExport.d.ts.map +1 -1
  136. package/dist/cli/tui/hooks/useExport.js +46 -40
  137. package/dist/cli/tui/hooks/useExport.js.map +1 -1
  138. package/dist/cli/tui/hooks/useRequests.d.ts +9 -3
  139. package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -1
  140. package/dist/cli/tui/hooks/useRequests.js +61 -15
  141. package/dist/cli/tui/hooks/useRequests.js.map +1 -1
  142. package/dist/cli/tui/hooks/useSaveBinary.d.ts +26 -0
  143. package/dist/cli/tui/hooks/useSaveBinary.d.ts.map +1 -0
  144. package/dist/cli/tui/hooks/useSaveBinary.js +165 -0
  145. package/dist/cli/tui/hooks/useSaveBinary.js.map +1 -0
  146. package/dist/cli/tui/hooks/useSpinner.d.ts +5 -0
  147. package/dist/cli/tui/hooks/useSpinner.d.ts.map +1 -0
  148. package/dist/cli/tui/hooks/useSpinner.js +25 -0
  149. package/dist/cli/tui/hooks/useSpinner.js.map +1 -0
  150. package/dist/cli/tui/utils/binary.d.ts +24 -0
  151. package/dist/cli/tui/utils/binary.d.ts.map +1 -0
  152. package/dist/cli/tui/utils/binary.js +152 -0
  153. package/dist/cli/tui/utils/binary.js.map +1 -0
  154. package/dist/cli/tui/utils/clipboard.d.ts +9 -0
  155. package/dist/cli/tui/utils/clipboard.d.ts.map +1 -0
  156. package/dist/cli/tui/utils/clipboard.js +58 -0
  157. package/dist/cli/tui/utils/clipboard.js.map +1 -0
  158. package/dist/cli/tui/utils/content-type.d.ts +8 -0
  159. package/dist/cli/tui/utils/content-type.d.ts.map +1 -0
  160. package/dist/cli/tui/utils/content-type.js +10 -0
  161. package/dist/cli/tui/utils/content-type.js.map +1 -0
  162. package/dist/cli/tui/utils/curl.d.ts.map +1 -1
  163. package/dist/cli/tui/utils/curl.js +9 -2
  164. package/dist/cli/tui/utils/curl.js.map +1 -1
  165. package/dist/cli/tui/utils/filters.d.ts +6 -0
  166. package/dist/cli/tui/utils/filters.d.ts.map +1 -0
  167. package/dist/cli/tui/utils/filters.js +13 -0
  168. package/dist/cli/tui/utils/filters.js.map +1 -0
  169. package/dist/cli/tui/utils/formatters.d.ts +8 -0
  170. package/dist/cli/tui/utils/formatters.d.ts.map +1 -1
  171. package/dist/cli/tui/utils/formatters.js +85 -0
  172. package/dist/cli/tui/utils/formatters.js.map +1 -1
  173. package/dist/cli/tui/utils/har.d.ts.map +1 -1
  174. package/dist/cli/tui/utils/har.js +3 -25
  175. package/dist/cli/tui/utils/har.js.map +1 -1
  176. package/dist/cli/tui/utils/json-tree.d.ts +69 -0
  177. package/dist/cli/tui/utils/json-tree.d.ts.map +1 -0
  178. package/dist/cli/tui/utils/json-tree.js +339 -0
  179. package/dist/cli/tui/utils/json-tree.js.map +1 -0
  180. package/dist/cli/tui/utils/open-external.d.ts +17 -0
  181. package/dist/cli/tui/utils/open-external.d.ts.map +1 -0
  182. package/dist/cli/tui/utils/open-external.js +57 -0
  183. package/dist/cli/tui/utils/open-external.js.map +1 -0
  184. package/dist/cli/tui/utils/syntax-highlight.d.ts +16 -0
  185. package/dist/cli/tui/utils/syntax-highlight.d.ts.map +1 -0
  186. package/dist/cli/tui/utils/syntax-highlight.js +64 -0
  187. package/dist/cli/tui/utils/syntax-highlight.js.map +1 -0
  188. package/dist/daemon/control.d.ts +3 -49
  189. package/dist/daemon/control.d.ts.map +1 -1
  190. package/dist/daemon/control.js +183 -141
  191. package/dist/daemon/control.js.map +1 -1
  192. package/dist/daemon/htpx-client.d.ts +8 -0
  193. package/dist/daemon/htpx-client.d.ts.map +1 -0
  194. package/dist/daemon/htpx-client.js +25 -0
  195. package/dist/daemon/htpx-client.js.map +1 -0
  196. package/dist/daemon/index.js +50 -2
  197. package/dist/daemon/index.js.map +1 -1
  198. package/dist/daemon/interceptor-loader.d.ts +30 -0
  199. package/dist/daemon/interceptor-loader.d.ts.map +1 -0
  200. package/dist/daemon/interceptor-loader.js +249 -0
  201. package/dist/daemon/interceptor-loader.js.map +1 -0
  202. package/dist/daemon/interceptor-runner.d.ts +39 -0
  203. package/dist/daemon/interceptor-runner.d.ts.map +1 -0
  204. package/dist/daemon/interceptor-runner.js +312 -0
  205. package/dist/daemon/interceptor-runner.js.map +1 -0
  206. package/dist/daemon/proxy.d.ts +12 -0
  207. package/dist/daemon/proxy.d.ts.map +1 -1
  208. package/dist/daemon/proxy.js +121 -10
  209. package/dist/daemon/proxy.js.map +1 -1
  210. package/dist/daemon/storage.d.ts +64 -2
  211. package/dist/daemon/storage.d.ts.map +1 -1
  212. package/dist/daemon/storage.js +527 -12
  213. package/dist/daemon/storage.js.map +1 -1
  214. package/dist/interceptors.d.ts +2 -0
  215. package/dist/interceptors.d.ts.map +1 -0
  216. package/dist/interceptors.js +2 -0
  217. package/dist/interceptors.js.map +1 -0
  218. package/dist/mcp/server.d.ts +110 -0
  219. package/dist/mcp/server.d.ts.map +1 -0
  220. package/dist/mcp/server.js +806 -0
  221. package/dist/mcp/server.js.map +1 -0
  222. package/dist/shared/config.d.ts +21 -0
  223. package/dist/shared/config.d.ts.map +1 -0
  224. package/dist/shared/config.js +83 -0
  225. package/dist/shared/config.js.map +1 -0
  226. package/dist/shared/content-type.d.ts +64 -0
  227. package/dist/shared/content-type.d.ts.map +1 -0
  228. package/dist/shared/content-type.js +145 -0
  229. package/dist/shared/content-type.js.map +1 -0
  230. package/dist/shared/control-client.d.ts +144 -0
  231. package/dist/shared/control-client.d.ts.map +1 -0
  232. package/dist/shared/control-client.js +272 -0
  233. package/dist/shared/control-client.js.map +1 -0
  234. package/dist/shared/daemon.d.ts.map +1 -1
  235. package/dist/shared/daemon.js +17 -4
  236. package/dist/shared/daemon.js.map +1 -1
  237. package/dist/shared/logger.d.ts +21 -5
  238. package/dist/shared/logger.d.ts.map +1 -1
  239. package/dist/shared/logger.js +100 -21
  240. package/dist/shared/logger.js.map +1 -1
  241. package/dist/shared/project.d.ts +16 -3
  242. package/dist/shared/project.d.ts.map +1 -1
  243. package/dist/shared/project.js +45 -5
  244. package/dist/shared/project.js.map +1 -1
  245. package/dist/shared/proxy-info.d.ts +10 -0
  246. package/dist/shared/proxy-info.d.ts.map +1 -0
  247. package/dist/shared/proxy-info.js +15 -0
  248. package/dist/shared/proxy-info.js.map +1 -0
  249. package/dist/shared/types.d.ts +95 -0
  250. package/dist/shared/types.d.ts.map +1 -1
  251. package/package.json +24 -5
  252. package/skills/htpx/SKILL.md +228 -0
@@ -0,0 +1,227 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Full-screen modal for viewing text body content in a less-style pager.
4
+ * Supports syntax highlighting, line numbers, search, and keyboard navigation.
5
+ * Replaces the main TUI when active (terminals don't support true overlays).
6
+ */
7
+ import React, { useState, useMemo, useCallback, useRef, useEffect } from "react";
8
+ import { Box, Text, useInput } from "ink";
9
+ import { highlightCode } from "../utils/syntax-highlight.js";
10
+ import { formatSize } from "../utils/formatters.js";
11
+ import { copyToClipboard } from "../utils/clipboard.js";
12
+ import { HintContent } from "./HintContent.js";
13
+ const TEXT_VIEWER_HINTS = [
14
+ { key: "j/k", action: "nav" },
15
+ { key: "^f/^b", action: "page" },
16
+ { key: "g/G", action: "top/bottom" },
17
+ { key: "/", action: "search" },
18
+ { key: "n/N", action: "match" },
19
+ { key: "y", action: "copy" },
20
+ { key: "q/Esc", action: "close" },
21
+ ];
22
+ const STATUS_MESSAGE_TIMEOUT_MS = 3000;
23
+ /** Rows reserved for header (title + info/search + divider), hint bar, and borders */
24
+ const HEADER_ROWS = 3;
25
+ const FOOTER_ROWS = 2;
26
+ export function TextViewerModal({ text, title, contentType, bodySize, width, height, onClose, isActive = true, onStatus, }) {
27
+ const [scrollOffset, setScrollOffset] = useState(0);
28
+ const [searchMode, setSearchMode] = useState(false);
29
+ const [searchText, setSearchText] = useState("");
30
+ const [currentMatchIndex, setCurrentMatchIndex] = useState(0);
31
+ const [statusMessage, setStatusMessage] = useState();
32
+ const statusTimeoutRef = useRef(null);
33
+ // Cleanup timers on unmount
34
+ useEffect(() => {
35
+ return () => {
36
+ if (statusTimeoutRef.current) {
37
+ clearTimeout(statusTimeoutRef.current);
38
+ }
39
+ };
40
+ }, []);
41
+ const showLocalStatus = useCallback((message) => {
42
+ if (statusTimeoutRef.current) {
43
+ clearTimeout(statusTimeoutRef.current);
44
+ }
45
+ setStatusMessage(message);
46
+ statusTimeoutRef.current = setTimeout(() => setStatusMessage(undefined), STATUS_MESSAGE_TIMEOUT_MS);
47
+ }, []);
48
+ // Prepare highlighted lines
49
+ const lines = useMemo(() => {
50
+ const highlighted = highlightCode(text, contentType);
51
+ return highlighted.split("\n");
52
+ }, [text, contentType]);
53
+ const totalLines = lines.length;
54
+ const lineNumberWidth = String(totalLines).length;
55
+ const availableHeight = height - HEADER_ROWS - FOOTER_ROWS;
56
+ // Compute search match line indices
57
+ const matchLineIndices = useMemo(() => {
58
+ if (!searchText)
59
+ return [];
60
+ const lowerSearch = searchText.toLowerCase();
61
+ const indices = [];
62
+ // Search against raw text lines (not highlighted) for accurate matching
63
+ const rawLines = text.split("\n");
64
+ for (let i = 0; i < rawLines.length; i++) {
65
+ const line = rawLines[i];
66
+ if (line !== undefined && line.toLowerCase().includes(lowerSearch)) {
67
+ indices.push(i);
68
+ }
69
+ }
70
+ return indices;
71
+ }, [text, searchText]);
72
+ // Keep currentMatchIndex in bounds when matches change
73
+ useEffect(() => {
74
+ if (matchLineIndices.length > 0 && currentMatchIndex >= matchLineIndices.length) {
75
+ setCurrentMatchIndex(0);
76
+ }
77
+ }, [matchLineIndices.length, currentMatchIndex]);
78
+ // Scroll helpers
79
+ const maxScrollOffset = Math.max(0, totalLines - availableHeight);
80
+ const scrollTo = useCallback((line) => {
81
+ const clamped = Math.max(0, Math.min(line, maxScrollOffset));
82
+ setScrollOffset(clamped);
83
+ }, [maxScrollOffset]);
84
+ // Auto-scroll to current match when using n/N
85
+ const scrollToMatch = useCallback((matchIdx) => {
86
+ if (matchLineIndices.length === 0)
87
+ return;
88
+ const lineIdx = matchLineIndices[matchIdx];
89
+ if (lineIdx === undefined)
90
+ return;
91
+ // Centre the match in the viewport if possible
92
+ const centreOffset = Math.max(0, lineIdx - Math.floor(availableHeight / 2));
93
+ scrollTo(centreOffset);
94
+ }, [matchLineIndices, availableHeight, scrollTo]);
95
+ useInput((input, key) => {
96
+ if (searchMode) {
97
+ if (key.escape) {
98
+ // Cancel search
99
+ setSearchMode(false);
100
+ setSearchText("");
101
+ setCurrentMatchIndex(0);
102
+ return;
103
+ }
104
+ if (key.return) {
105
+ // Close search mode, jump to first match
106
+ setSearchMode(false);
107
+ if (matchLineIndices.length > 0) {
108
+ setCurrentMatchIndex(0);
109
+ scrollToMatch(0);
110
+ }
111
+ return;
112
+ }
113
+ if (key.backspace || key.delete) {
114
+ setSearchText((prev) => prev.slice(0, -1));
115
+ return;
116
+ }
117
+ // Only accept single printable characters
118
+ if (input && input.length === 1 && !key.ctrl && !key.meta) {
119
+ setSearchText((prev) => prev + input);
120
+ }
121
+ return;
122
+ }
123
+ // Normal mode
124
+ if (key.escape || input === "q") {
125
+ onClose();
126
+ return;
127
+ }
128
+ if (input === "j" || key.downArrow) {
129
+ setScrollOffset((prev) => Math.min(prev + 1, maxScrollOffset));
130
+ }
131
+ else if (input === "k" || key.upArrow) {
132
+ setScrollOffset((prev) => Math.max(prev - 1, 0));
133
+ }
134
+ else if (input === "d" && key.ctrl) {
135
+ const halfPage = Math.floor(availableHeight / 2);
136
+ setScrollOffset((prev) => Math.min(prev + halfPage, maxScrollOffset));
137
+ }
138
+ else if (input === "u" && key.ctrl) {
139
+ const halfPage = Math.floor(availableHeight / 2);
140
+ setScrollOffset((prev) => Math.max(prev - halfPage, 0));
141
+ }
142
+ else if ((input === "f" && key.ctrl) || input === " ") {
143
+ // Full-page down (Ctrl+f or Space)
144
+ setScrollOffset((prev) => Math.min(prev + availableHeight, maxScrollOffset));
145
+ }
146
+ else if (input === "b" && key.ctrl) {
147
+ // Full-page up
148
+ setScrollOffset((prev) => Math.max(prev - availableHeight, 0));
149
+ }
150
+ else if (input === "g" && !key.shift) {
151
+ setScrollOffset(0);
152
+ }
153
+ else if (input === "G") {
154
+ setScrollOffset(maxScrollOffset);
155
+ }
156
+ else if (input === "/") {
157
+ setSearchMode(true);
158
+ setSearchText("");
159
+ setCurrentMatchIndex(0);
160
+ }
161
+ else if (input === "n") {
162
+ // Next match
163
+ if (matchLineIndices.length > 0) {
164
+ const nextIdx = (currentMatchIndex + 1) % matchLineIndices.length;
165
+ setCurrentMatchIndex(nextIdx);
166
+ scrollToMatch(nextIdx);
167
+ }
168
+ }
169
+ else if (input === "N") {
170
+ // Previous match
171
+ if (matchLineIndices.length > 0) {
172
+ const prevIdx = (currentMatchIndex - 1 + matchLineIndices.length) % matchLineIndices.length;
173
+ setCurrentMatchIndex(prevIdx);
174
+ scrollToMatch(prevIdx);
175
+ }
176
+ }
177
+ else if (input === "y") {
178
+ void copyToClipboard(text).then(() => {
179
+ const msg = "Copied to clipboard";
180
+ showLocalStatus(msg);
181
+ onStatus?.(msg);
182
+ }, () => {
183
+ const msg = "Failed to copy to clipboard";
184
+ showLocalStatus(msg);
185
+ onStatus?.(msg);
186
+ });
187
+ }
188
+ }, { isActive });
189
+ // Content type short display
190
+ const shortCt = contentType.split(";")[0]?.trim() ?? "";
191
+ // Build visible row slice
192
+ const visibleSlice = lines.slice(scrollOffset, scrollOffset + availableHeight);
193
+ // Determine the current match line for highlighting
194
+ const currentMatchLine = matchLineIndices.length > 0 ? matchLineIndices[currentMatchIndex] : undefined;
195
+ // Set of all match lines for quick lookup
196
+ const matchLineSet = useMemo(() => new Set(matchLineIndices), [matchLineIndices]);
197
+ // Header border
198
+ const headerRight = ` ${shortCt} ${formatSize(bodySize)} `;
199
+ const titlePart = ` ${title} `;
200
+ const headerBorderWidth = width - titlePart.length - headerRight.length - 4;
201
+ const headerBorder = `\u250C\u2500${titlePart}${"\u2500".repeat(Math.max(0, headerBorderWidth))}${headerRight}\u2500\u2510`;
202
+ // Divider
203
+ const divider = `\u251C${"\u2500".repeat(width - 2)}\u2524`;
204
+ // Footer border
205
+ const footerBorder = `\u2514${"\u2500".repeat(width - 2)}\u2518`;
206
+ // Content width available for text (minus line number column, separator, padding)
207
+ const textAreaWidth = width - 4 - lineNumberWidth - 1;
208
+ return (_jsxs(Box, { flexDirection: "column", width: width, height: height, children: [_jsx(Text, { color: "cyan", children: headerBorder }), _jsx(Box, { paddingX: 1, height: 1, children: searchMode ? (_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "search: " }), _jsx(Text, { children: searchText }), _jsx(Text, { color: "gray", children: "\u2588" })] })) : (_jsx(Text, { dimColor: true, wrap: "truncate", children: matchLineIndices.length > 0
209
+ ? `Line ${scrollOffset + 1}/${totalLines} | ${matchLineIndices.length} match${matchLineIndices.length === 1 ? "" : "es"} (${currentMatchIndex + 1}/${matchLineIndices.length})`
210
+ : `Line ${scrollOffset + 1}/${totalLines}` })) }), _jsx(Text, { color: "cyan", children: divider }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [visibleSlice.map((line, idx) => {
211
+ const globalLineIdx = scrollOffset + idx;
212
+ const lineNumber = globalLineIdx + 1;
213
+ const isMatch = matchLineSet.has(globalLineIdx);
214
+ const isCurrentMatch = globalLineIdx === currentMatchLine;
215
+ return (_jsx(TextLine, { lineNumber: lineNumber, lineNumberWidth: lineNumberWidth, text: line, textAreaWidth: textAreaWidth, isMatch: isMatch, isCurrentMatch: isCurrentMatch }, globalLineIdx));
216
+ }), visibleSlice.length < availableHeight && _jsx(Box, { flexGrow: 1 })] }), _jsx(Text, { color: "cyan", children: divider }), _jsx(Box, { paddingX: 1, height: 1, children: statusMessage ? (_jsx(Text, { color: "green", children: statusMessage })) : (_jsx(HintContent, { hints: TEXT_VIEWER_HINTS, availableWidth: width - 4 })) }), _jsx(Text, { color: "cyan", children: footerBorder })] }));
217
+ }
218
+ /**
219
+ * Single line of text with line number gutter.
220
+ */
221
+ const TextLine = React.memo(function TextLine({ lineNumber, lineNumberWidth, text, textAreaWidth, isMatch, isCurrentMatch, }) {
222
+ const paddedLineNumber = String(lineNumber).padStart(lineNumberWidth, " ");
223
+ // Truncate line if wider than available space
224
+ const displayText = text.length > textAreaWidth ? text.slice(0, textAreaWidth) : text;
225
+ return (_jsxs(Text, { children: [_jsx(Text, { color: isCurrentMatch ? "yellow" : isMatch ? "yellow" : undefined, bold: isCurrentMatch, dimColor: !isMatch && !isCurrentMatch, children: paddedLineNumber }), _jsxs(Text, { dimColor: true, children: [" ", "\u2502", " "] }), _jsx(Text, { children: displayText })] }));
226
+ });
227
+ //# sourceMappingURL=TextViewerModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextViewerModal.js","sourceRoot":"","sources":["../../../../src/cli/tui/components/TextViewerModal.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Hook for saving body content to disk.
3
+ */
4
+ export type SaveLocation = "exports" | "downloads" | "custom";
5
+ export interface ExportResult {
6
+ success: boolean;
7
+ message: string;
8
+ filePath?: string;
9
+ }
10
+ /**
11
+ * Generate a filename for body content based on request metadata.
12
+ */
13
+ export declare function generateFilename(requestId: string, contentType: string | undefined, url: string): string;
14
+ /**
15
+ * Save body content to disk.
16
+ */
17
+ export declare function saveBodyContent(body: Buffer, filename: string, location: SaveLocation, customPath?: string): Promise<ExportResult>;
18
+ interface UseBodyExportResult {
19
+ saveBody: (body: Buffer, requestId: string, contentType: string | undefined, url: string, location: SaveLocation, customPath?: string) => Promise<ExportResult>;
20
+ }
21
+ /**
22
+ * Hook providing body export/save functionality.
23
+ */
24
+ export declare function useBodyExport(): UseBodyExportResult;
25
+ export {};
26
+ //# sourceMappingURL=useBodyExport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBodyExport.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useBodyExport.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,GAAG,EAAE,MAAM,GACV,MAAM,CAuBR;AA4GD;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CA6BvB;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,YAAY,EACtB,UAAU,CAAC,EAAE,MAAM,KAChB,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,mBAAmB,CAiBnD"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Hook for saving body content to disk.
3
+ */
4
+ import { useCallback } from "react";
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ import * as os from "node:os";
8
+ import { copyToClipboard } from "../utils/clipboard.js";
9
+ import { findOrCreateProjectRoot, ensureHtpxDir } from "../../../shared/project.js";
10
+ /**
11
+ * Generate a filename for body content based on request metadata.
12
+ */
13
+ export function generateFilename(requestId, contentType, url) {
14
+ // Try to extract extension from URL first
15
+ let extension = "";
16
+ try {
17
+ const urlPath = new URL(url).pathname;
18
+ const urlExtMatch = urlPath.match(/\.([a-zA-Z0-9]+)$/);
19
+ if (urlExtMatch?.[1]) {
20
+ extension = urlExtMatch[1].toLowerCase();
21
+ }
22
+ }
23
+ catch {
24
+ // Invalid URL, fall through to content-type detection
25
+ }
26
+ if (!extension && contentType) {
27
+ extension = getExtensionFromContentType(contentType);
28
+ }
29
+ // Generate timestamp for uniqueness
30
+ const timestamp = Date.now();
31
+ const shortId = requestId.slice(0, 8);
32
+ return extension ? `${shortId}-${timestamp}.${extension}` : `${shortId}-${timestamp}.bin`;
33
+ }
34
+ /**
35
+ * Map common content types to file extensions.
36
+ */
37
+ function getExtensionFromContentType(contentType) {
38
+ const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
39
+ const mappings = {
40
+ // Text
41
+ "application/json": "json",
42
+ "text/html": "html",
43
+ "text/css": "css",
44
+ "text/javascript": "js",
45
+ "application/javascript": "js",
46
+ "text/xml": "xml",
47
+ "application/xml": "xml",
48
+ "text/plain": "txt",
49
+ "text/csv": "csv",
50
+ // Images
51
+ "image/jpeg": "jpg",
52
+ "image/png": "png",
53
+ "image/gif": "gif",
54
+ "image/webp": "webp",
55
+ "image/svg+xml": "svg",
56
+ "image/x-icon": "ico",
57
+ "image/bmp": "bmp",
58
+ "image/tiff": "tiff",
59
+ // Audio
60
+ "audio/mpeg": "mp3",
61
+ "audio/wav": "wav",
62
+ "audio/ogg": "ogg",
63
+ "audio/webm": "weba",
64
+ "audio/aac": "aac",
65
+ "audio/flac": "flac",
66
+ // Video
67
+ "video/mp4": "mp4",
68
+ "video/webm": "webm",
69
+ "video/ogg": "ogv",
70
+ "video/quicktime": "mov",
71
+ "video/x-msvideo": "avi",
72
+ "video/x-matroska": "mkv",
73
+ // Documents
74
+ "application/pdf": "pdf",
75
+ "application/msword": "doc",
76
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
77
+ "application/vnd.ms-excel": "xls",
78
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
79
+ "application/vnd.ms-powerpoint": "ppt",
80
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
81
+ // Archives
82
+ "application/zip": "zip",
83
+ "application/gzip": "gz",
84
+ "application/x-tar": "tar",
85
+ "application/x-rar-compressed": "rar",
86
+ "application/x-7z-compressed": "7z",
87
+ // Other
88
+ "application/octet-stream": "bin",
89
+ "application/wasm": "wasm",
90
+ };
91
+ return mappings[type] ?? "bin";
92
+ }
93
+ /**
94
+ * Resolve the target directory based on save location.
95
+ */
96
+ function resolveTargetDir(location, customPath) {
97
+ switch (location) {
98
+ case "exports": {
99
+ const projectRoot = findOrCreateProjectRoot();
100
+ const htpxDir = ensureHtpxDir(projectRoot);
101
+ const exportsDir = path.join(htpxDir, "exports");
102
+ if (!fs.existsSync(exportsDir)) {
103
+ fs.mkdirSync(exportsDir, { recursive: true });
104
+ }
105
+ return exportsDir;
106
+ }
107
+ case "downloads": {
108
+ const downloadsDir = path.join(os.homedir(), "Downloads");
109
+ if (!fs.existsSync(downloadsDir)) {
110
+ fs.mkdirSync(downloadsDir, { recursive: true });
111
+ }
112
+ return downloadsDir;
113
+ }
114
+ case "custom": {
115
+ if (!customPath) {
116
+ throw new Error("Custom path required");
117
+ }
118
+ // Expand ~ to home directory
119
+ const expandedPath = customPath.startsWith("~")
120
+ ? path.join(os.homedir(), customPath.slice(1))
121
+ : customPath;
122
+ const resolvedPath = path.resolve(expandedPath);
123
+ if (!fs.existsSync(resolvedPath)) {
124
+ fs.mkdirSync(resolvedPath, { recursive: true });
125
+ }
126
+ return resolvedPath;
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * Save body content to disk.
132
+ */
133
+ export async function saveBodyContent(body, filename, location, customPath) {
134
+ try {
135
+ const targetDir = resolveTargetDir(location, customPath);
136
+ const filePath = path.join(targetDir, filename);
137
+ fs.writeFileSync(filePath, body);
138
+ // Copy path to clipboard
139
+ try {
140
+ await copyToClipboard(filePath);
141
+ return {
142
+ success: true,
143
+ message: `Saved to ${filePath} (path copied)`,
144
+ filePath,
145
+ };
146
+ }
147
+ catch {
148
+ // Clipboard failed but file was saved
149
+ return {
150
+ success: true,
151
+ message: `Saved to ${filePath}`,
152
+ filePath,
153
+ };
154
+ }
155
+ }
156
+ catch (err) {
157
+ return {
158
+ success: false,
159
+ message: err instanceof Error ? err.message : "Failed to save file",
160
+ };
161
+ }
162
+ }
163
+ /**
164
+ * Hook providing body export/save functionality.
165
+ */
166
+ export function useBodyExport() {
167
+ const saveBody = useCallback(async (body, requestId, contentType, url, location, customPath) => {
168
+ const filename = generateFilename(requestId, contentType, url);
169
+ return saveBodyContent(body, filename, location, customPath);
170
+ }, []);
171
+ return { saveBody };
172
+ }
173
+ //# sourceMappingURL=useBodyExport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBodyExport.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useBodyExport.ts"],"names":[],"mappings":"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"}
@@ -2,16 +2,27 @@
2
2
  * Hook for exporting captured requests to various formats.
3
3
  */
4
4
  import type { CapturedRequest } from "../../../shared/types.js";
5
- interface ExportResult {
5
+ export interface ExportResult {
6
6
  success: boolean;
7
7
  message: string;
8
8
  }
9
9
  interface UseExportResult {
10
- exportCurl: (request: CapturedRequest) => ExportResult;
10
+ exportCurl: (request: CapturedRequest) => Promise<ExportResult>;
11
11
  exportHar: (requests: CapturedRequest[], filename?: string) => ExportResult;
12
12
  }
13
+ /**
14
+ * Generate curl command for a request and copy to clipboard.
15
+ * Extracted as standalone function for testability.
16
+ */
17
+ export declare function exportCurlToClipboard(request: CapturedRequest): Promise<ExportResult>;
18
+ /**
19
+ * Export requests to HAR file.
20
+ * Extracted as standalone function for testability.
21
+ */
22
+ export declare function exportHarToFile(requests: CapturedRequest[], filename?: string): ExportResult;
13
23
  /**
14
24
  * Hook providing export functionality for captured requests.
25
+ * Wraps standalone functions with useCallback for React optimisation.
15
26
  */
16
27
  export declare function useExport(): UseExportResult;
17
28
  export {};
@@ -1 +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"}
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;AAKhE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,eAAe;IACvB,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAChE,SAAS,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,YAAY,CAAC;CAC7E;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAc3F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAkB5F;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAY3C"}
@@ -6,50 +6,56 @@ import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
7
  import { generateCurl } from "../utils/curl.js";
8
8
  import { generateHarString } from "../utils/har.js";
9
+ import { copyToClipboard } from "../utils/clipboard.js";
10
+ /**
11
+ * Generate curl command for a request and copy to clipboard.
12
+ * Extracted as standalone function for testability.
13
+ */
14
+ export async function exportCurlToClipboard(request) {
15
+ try {
16
+ const curl = generateCurl(request);
17
+ await copyToClipboard(curl);
18
+ return {
19
+ success: true,
20
+ message: "Copied to clipboard",
21
+ };
22
+ }
23
+ catch (err) {
24
+ return {
25
+ success: false,
26
+ message: err instanceof Error ? err.message : "Failed to copy curl",
27
+ };
28
+ }
29
+ }
30
+ /**
31
+ * Export requests to HAR file.
32
+ * Extracted as standalone function for testability.
33
+ */
34
+ export function exportHarToFile(requests, filename) {
35
+ try {
36
+ const harFilename = filename ?? `htpx-export-${Date.now()}.har`;
37
+ const harPath = path.resolve(process.cwd(), harFilename);
38
+ const harContent = generateHarString(requests);
39
+ fs.writeFileSync(harPath, harContent, "utf-8");
40
+ return {
41
+ success: true,
42
+ message: `Exported ${requests.length} request(s) to ${harPath}`,
43
+ };
44
+ }
45
+ catch (err) {
46
+ return {
47
+ success: false,
48
+ message: err instanceof Error ? err.message : "Failed to export HAR",
49
+ };
50
+ }
51
+ }
9
52
  /**
10
53
  * Hook providing export functionality for captured requests.
54
+ * Wraps standalone functions with useCallback for React optimisation.
11
55
  */
12
56
  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
- }, []);
57
+ const exportCurl = useCallback((request) => exportCurlToClipboard(request), []);
58
+ const exportHar = useCallback((requests, filename) => exportHarToFile(requests, filename), []);
53
59
  return {
54
60
  exportCurl,
55
61
  exportHar,
@@ -1 +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"}
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;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAYxD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAwB;IAClE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;QAC5B,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,qBAAqB;SAC/B,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;SACpE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAA2B,EAAE,QAAiB;IAC5E,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,QAAQ,IAAI,eAAe,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE/C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QAE/C,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,YAAY,QAAQ,CAAC,MAAM,kBAAkB,OAAO,EAAE;SAChE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB;SACrE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAEjG,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,QAA2B,EAAE,QAAiB,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,EACvF,EAAE,CACH,CAAC;IAEF,OAAO;QACL,UAAU;QACV,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -1,16 +1,22 @@
1
1
  /**
2
2
  * Hook for fetching and polling captured requests from the daemon.
3
3
  */
4
- import type { CapturedRequest } from "../../../shared/types.js";
4
+ import type { CapturedRequest, CapturedRequestSummary, RequestFilter } from "../../../shared/types.js";
5
5
  interface UseRequestsOptions {
6
- label?: string;
7
6
  pollInterval?: number;
7
+ filter?: RequestFilter;
8
+ projectRoot?: string;
8
9
  }
9
10
  interface UseRequestsResult {
10
- requests: CapturedRequest[];
11
+ /** Request summaries for list display (excludes body/header data) */
12
+ requests: CapturedRequestSummary[];
11
13
  isLoading: boolean;
12
14
  error: string | null;
13
15
  refresh: () => Promise<void>;
16
+ /** Fetch full request data including body/headers */
17
+ getFullRequest: (id: string) => Promise<CapturedRequest | null>;
18
+ /** Fetch all requests with full data (for exports) */
19
+ getAllFullRequests: () => Promise<CapturedRequest[]>;
14
20
  }
15
21
  /**
16
22
  * Hook to fetch and poll for captured requests.
@@ -1 +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"}
1
+ {"version":3,"file":"useRequests.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useRequests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAOlC,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,qEAAqE;IACrE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,qDAAqD;IACrD,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAChE,sDAAsD;IACtD,kBAAkB,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAmI/E"}