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.
- package/LICENSE +665 -21
- package/README.md +422 -116
- package/dist/cli/commands/clear.d.ts.map +1 -1
- package/dist/cli/commands/clear.js +11 -11
- package/dist/cli/commands/clear.js.map +1 -1
- package/dist/cli/commands/daemon.d.ts +3 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +59 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/debug-dump.d.ts.map +1 -1
- package/dist/cli/commands/debug-dump.js +8 -10
- package/dist/cli/commands/debug-dump.js.map +1 -1
- package/dist/cli/commands/helpers.d.ts +18 -0
- package/dist/cli/commands/helpers.d.ts.map +1 -0
- package/dist/cli/commands/helpers.js +34 -0
- package/dist/cli/commands/helpers.js.map +1 -0
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -4
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/intercept.d.ts +2 -1
- package/dist/cli/commands/intercept.d.ts.map +1 -1
- package/dist/cli/commands/intercept.js +74 -30
- package/dist/cli/commands/intercept.js.map +1 -1
- package/dist/cli/commands/interceptors.d.ts +3 -0
- package/dist/cli/commands/interceptors.d.ts.map +1 -0
- package/dist/cli/commands/interceptors.js +163 -0
- package/dist/cli/commands/interceptors.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +24 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/off.d.ts +8 -0
- package/dist/cli/commands/off.d.ts.map +1 -0
- package/dist/cli/commands/off.js +34 -0
- package/dist/cli/commands/off.js.map +1 -0
- package/dist/cli/commands/on.d.ts +9 -0
- package/dist/cli/commands/on.d.ts.map +1 -0
- package/dist/cli/commands/on.js +121 -0
- package/dist/cli/commands/on.js.map +1 -0
- package/dist/cli/commands/project.d.ts.map +1 -1
- package/dist/cli/commands/project.js +5 -3
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/restart.d.ts.map +1 -1
- package/dist/cli/commands/restart.js +5 -10
- package/dist/cli/commands/restart.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +50 -20
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +7 -10
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/commands/tui.d.ts.map +1 -1
- package/dist/cli/commands/tui.js +6 -13
- package/dist/cli/commands/tui.js.map +1 -1
- package/dist/cli/index.js +12 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tui/App.d.ts +7 -2
- package/dist/cli/tui/App.d.ts.map +1 -1
- package/dist/cli/tui/App.js +490 -33
- package/dist/cli/tui/App.js.map +1 -1
- package/dist/cli/tui/components/AccordionContent.d.ts +28 -0
- package/dist/cli/tui/components/AccordionContent.d.ts.map +1 -0
- package/dist/cli/tui/components/AccordionContent.js +87 -0
- package/dist/cli/tui/components/AccordionContent.js.map +1 -0
- package/dist/cli/tui/components/AccordionPanel.d.ts +38 -0
- package/dist/cli/tui/components/AccordionPanel.d.ts.map +1 -0
- package/dist/cli/tui/components/AccordionPanel.js +110 -0
- package/dist/cli/tui/components/AccordionPanel.js.map +1 -0
- package/dist/cli/tui/components/AccordionSection.d.ts +32 -0
- package/dist/cli/tui/components/AccordionSection.d.ts.map +1 -0
- package/dist/cli/tui/components/AccordionSection.js +41 -0
- package/dist/cli/tui/components/AccordionSection.js.map +1 -0
- package/dist/cli/tui/components/ExportModal.d.ts +34 -0
- package/dist/cli/tui/components/ExportModal.d.ts.map +1 -0
- package/dist/cli/tui/components/ExportModal.js +109 -0
- package/dist/cli/tui/components/ExportModal.js.map +1 -0
- package/dist/cli/tui/components/FilterBar.d.ts +21 -0
- package/dist/cli/tui/components/FilterBar.d.ts.map +1 -0
- package/dist/cli/tui/components/FilterBar.js +155 -0
- package/dist/cli/tui/components/FilterBar.js.map +1 -0
- package/dist/cli/tui/components/HelpModal.d.ts +13 -0
- package/dist/cli/tui/components/HelpModal.d.ts.map +1 -0
- package/dist/cli/tui/components/HelpModal.js +78 -0
- package/dist/cli/tui/components/HelpModal.js.map +1 -0
- package/dist/cli/tui/components/HintContent.d.ts +25 -0
- package/dist/cli/tui/components/HintContent.d.ts.map +1 -0
- package/dist/cli/tui/components/HintContent.js +44 -0
- package/dist/cli/tui/components/HintContent.js.map +1 -0
- package/dist/cli/tui/components/InfoModal.d.ts +15 -0
- package/dist/cli/tui/components/InfoModal.d.ts.map +1 -0
- package/dist/cli/tui/components/InfoModal.js +17 -0
- package/dist/cli/tui/components/InfoModal.js.map +1 -0
- package/dist/cli/tui/components/JsonExplorerModal.d.ts +24 -0
- package/dist/cli/tui/components/JsonExplorerModal.d.ts.map +1 -0
- package/dist/cli/tui/components/JsonExplorerModal.js +311 -0
- package/dist/cli/tui/components/JsonExplorerModal.js.map +1 -0
- package/dist/cli/tui/components/Modal.d.ts +26 -0
- package/dist/cli/tui/components/Modal.d.ts.map +1 -0
- package/dist/cli/tui/components/Modal.js +15 -0
- package/dist/cli/tui/components/Modal.js.map +1 -0
- package/dist/cli/tui/components/Panel.d.ts +19 -0
- package/dist/cli/tui/components/Panel.d.ts.map +1 -0
- package/dist/cli/tui/components/Panel.js +37 -0
- package/dist/cli/tui/components/Panel.js.map +1 -0
- package/dist/cli/tui/components/RequestDetails.d.ts +4 -1
- package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -1
- package/dist/cli/tui/components/RequestDetails.js +9 -5
- package/dist/cli/tui/components/RequestDetails.js.map +1 -1
- package/dist/cli/tui/components/RequestList.d.ts +9 -3
- package/dist/cli/tui/components/RequestList.d.ts.map +1 -1
- package/dist/cli/tui/components/RequestList.js +24 -11
- package/dist/cli/tui/components/RequestList.js.map +1 -1
- package/dist/cli/tui/components/RequestListItem.d.ts +26 -3
- package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -1
- package/dist/cli/tui/components/RequestListItem.js +86 -9
- package/dist/cli/tui/components/RequestListItem.js.map +1 -1
- package/dist/cli/tui/components/SaveModal.d.ts +30 -0
- package/dist/cli/tui/components/SaveModal.d.ts.map +1 -0
- package/dist/cli/tui/components/SaveModal.js +95 -0
- package/dist/cli/tui/components/SaveModal.js.map +1 -0
- package/dist/cli/tui/components/StatusBar.d.ts +31 -2
- package/dist/cli/tui/components/StatusBar.d.ts.map +1 -1
- package/dist/cli/tui/components/StatusBar.js +44 -9
- package/dist/cli/tui/components/StatusBar.js.map +1 -1
- package/dist/cli/tui/components/TextViewerModal.d.ts +19 -0
- package/dist/cli/tui/components/TextViewerModal.d.ts.map +1 -0
- package/dist/cli/tui/components/TextViewerModal.js +227 -0
- package/dist/cli/tui/components/TextViewerModal.js.map +1 -0
- package/dist/cli/tui/hooks/useBodyExport.d.ts +26 -0
- package/dist/cli/tui/hooks/useBodyExport.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useBodyExport.js +173 -0
- package/dist/cli/tui/hooks/useBodyExport.js.map +1 -0
- package/dist/cli/tui/hooks/useExport.d.ts +13 -2
- package/dist/cli/tui/hooks/useExport.d.ts.map +1 -1
- package/dist/cli/tui/hooks/useExport.js +46 -40
- package/dist/cli/tui/hooks/useExport.js.map +1 -1
- package/dist/cli/tui/hooks/useRequests.d.ts +9 -3
- package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -1
- package/dist/cli/tui/hooks/useRequests.js +61 -15
- package/dist/cli/tui/hooks/useRequests.js.map +1 -1
- package/dist/cli/tui/hooks/useSaveBinary.d.ts +26 -0
- package/dist/cli/tui/hooks/useSaveBinary.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useSaveBinary.js +165 -0
- package/dist/cli/tui/hooks/useSaveBinary.js.map +1 -0
- package/dist/cli/tui/hooks/useSpinner.d.ts +5 -0
- package/dist/cli/tui/hooks/useSpinner.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useSpinner.js +25 -0
- package/dist/cli/tui/hooks/useSpinner.js.map +1 -0
- package/dist/cli/tui/utils/binary.d.ts +24 -0
- package/dist/cli/tui/utils/binary.d.ts.map +1 -0
- package/dist/cli/tui/utils/binary.js +152 -0
- package/dist/cli/tui/utils/binary.js.map +1 -0
- package/dist/cli/tui/utils/clipboard.d.ts +9 -0
- package/dist/cli/tui/utils/clipboard.d.ts.map +1 -0
- package/dist/cli/tui/utils/clipboard.js +58 -0
- package/dist/cli/tui/utils/clipboard.js.map +1 -0
- package/dist/cli/tui/utils/content-type.d.ts +8 -0
- package/dist/cli/tui/utils/content-type.d.ts.map +1 -0
- package/dist/cli/tui/utils/content-type.js +10 -0
- package/dist/cli/tui/utils/content-type.js.map +1 -0
- package/dist/cli/tui/utils/curl.d.ts.map +1 -1
- package/dist/cli/tui/utils/curl.js +9 -2
- package/dist/cli/tui/utils/curl.js.map +1 -1
- package/dist/cli/tui/utils/filters.d.ts +6 -0
- package/dist/cli/tui/utils/filters.d.ts.map +1 -0
- package/dist/cli/tui/utils/filters.js +13 -0
- package/dist/cli/tui/utils/filters.js.map +1 -0
- package/dist/cli/tui/utils/formatters.d.ts +8 -0
- package/dist/cli/tui/utils/formatters.d.ts.map +1 -1
- package/dist/cli/tui/utils/formatters.js +85 -0
- package/dist/cli/tui/utils/formatters.js.map +1 -1
- package/dist/cli/tui/utils/har.d.ts.map +1 -1
- package/dist/cli/tui/utils/har.js +3 -25
- package/dist/cli/tui/utils/har.js.map +1 -1
- package/dist/cli/tui/utils/json-tree.d.ts +69 -0
- package/dist/cli/tui/utils/json-tree.d.ts.map +1 -0
- package/dist/cli/tui/utils/json-tree.js +339 -0
- package/dist/cli/tui/utils/json-tree.js.map +1 -0
- package/dist/cli/tui/utils/open-external.d.ts +17 -0
- package/dist/cli/tui/utils/open-external.d.ts.map +1 -0
- package/dist/cli/tui/utils/open-external.js +57 -0
- package/dist/cli/tui/utils/open-external.js.map +1 -0
- package/dist/cli/tui/utils/syntax-highlight.d.ts +16 -0
- package/dist/cli/tui/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/cli/tui/utils/syntax-highlight.js +64 -0
- package/dist/cli/tui/utils/syntax-highlight.js.map +1 -0
- package/dist/daemon/control.d.ts +3 -49
- package/dist/daemon/control.d.ts.map +1 -1
- package/dist/daemon/control.js +183 -141
- package/dist/daemon/control.js.map +1 -1
- package/dist/daemon/htpx-client.d.ts +8 -0
- package/dist/daemon/htpx-client.d.ts.map +1 -0
- package/dist/daemon/htpx-client.js +25 -0
- package/dist/daemon/htpx-client.js.map +1 -0
- package/dist/daemon/index.js +50 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/interceptor-loader.d.ts +30 -0
- package/dist/daemon/interceptor-loader.d.ts.map +1 -0
- package/dist/daemon/interceptor-loader.js +249 -0
- package/dist/daemon/interceptor-loader.js.map +1 -0
- package/dist/daemon/interceptor-runner.d.ts +39 -0
- package/dist/daemon/interceptor-runner.d.ts.map +1 -0
- package/dist/daemon/interceptor-runner.js +312 -0
- package/dist/daemon/interceptor-runner.js.map +1 -0
- package/dist/daemon/proxy.d.ts +12 -0
- package/dist/daemon/proxy.d.ts.map +1 -1
- package/dist/daemon/proxy.js +121 -10
- package/dist/daemon/proxy.js.map +1 -1
- package/dist/daemon/storage.d.ts +64 -2
- package/dist/daemon/storage.d.ts.map +1 -1
- package/dist/daemon/storage.js +527 -12
- package/dist/daemon/storage.js.map +1 -1
- package/dist/interceptors.d.ts +2 -0
- package/dist/interceptors.d.ts.map +1 -0
- package/dist/interceptors.js +2 -0
- package/dist/interceptors.js.map +1 -0
- package/dist/mcp/server.d.ts +110 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +806 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/shared/config.d.ts +21 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +83 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/content-type.d.ts +64 -0
- package/dist/shared/content-type.d.ts.map +1 -0
- package/dist/shared/content-type.js +145 -0
- package/dist/shared/content-type.js.map +1 -0
- package/dist/shared/control-client.d.ts +144 -0
- package/dist/shared/control-client.d.ts.map +1 -0
- package/dist/shared/control-client.js +272 -0
- package/dist/shared/control-client.js.map +1 -0
- package/dist/shared/daemon.d.ts.map +1 -1
- package/dist/shared/daemon.js +17 -4
- package/dist/shared/daemon.js.map +1 -1
- package/dist/shared/logger.d.ts +21 -5
- package/dist/shared/logger.d.ts.map +1 -1
- package/dist/shared/logger.js +100 -21
- package/dist/shared/logger.js.map +1 -1
- package/dist/shared/project.d.ts +16 -3
- package/dist/shared/project.d.ts.map +1 -1
- package/dist/shared/project.js +45 -5
- package/dist/shared/project.js.map +1 -1
- package/dist/shared/proxy-info.d.ts +10 -0
- package/dist/shared/proxy-info.d.ts.map +1 -0
- package/dist/shared/proxy-info.js +15 -0
- package/dist/shared/proxy-info.js.map +1 -0
- package/dist/shared/types.d.ts +95 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +24 -5
- package/skills/htpx/SKILL.md +228 -0
|
@@ -2,43 +2,55 @@
|
|
|
2
2
|
* Hook for fetching and polling captured requests from the daemon.
|
|
3
3
|
*/
|
|
4
4
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
5
|
-
import { ControlClient } from "../../../
|
|
5
|
+
import { ControlClient } from "../../../shared/control-client.js";
|
|
6
6
|
import { findProjectRoot, getHtpxPaths } from "../../../shared/project.js";
|
|
7
|
+
const DEFAULT_QUERY_LIMIT = 1000;
|
|
8
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
7
9
|
/**
|
|
8
10
|
* Hook to fetch and poll for captured requests.
|
|
9
11
|
*/
|
|
10
12
|
export function useRequests(options = {}) {
|
|
11
|
-
const {
|
|
13
|
+
const { pollInterval = DEFAULT_POLL_INTERVAL_MS, filter, projectRoot } = options;
|
|
12
14
|
const [requests, setRequests] = useState([]);
|
|
13
15
|
const [isLoading, setIsLoading] = useState(true);
|
|
14
16
|
const [error, setError] = useState(null);
|
|
15
17
|
const clientRef = useRef(null);
|
|
16
18
|
const lastCountRef = useRef(0);
|
|
19
|
+
const requestsLengthRef = useRef(0);
|
|
20
|
+
const filterRef = useRef(filter);
|
|
17
21
|
// Initialise control client
|
|
18
22
|
useEffect(() => {
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
23
|
+
const resolvedRoot = projectRoot ?? findProjectRoot();
|
|
24
|
+
if (!resolvedRoot) {
|
|
21
25
|
setError("Not in an htpx project. Run 'htpx init' first.");
|
|
22
26
|
setIsLoading(false);
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
|
-
const paths = getHtpxPaths(
|
|
29
|
+
const paths = getHtpxPaths(resolvedRoot);
|
|
26
30
|
clientRef.current = new ControlClient(paths.controlSocketFile);
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
return () => {
|
|
32
|
+
clientRef.current?.close();
|
|
33
|
+
};
|
|
34
|
+
}, [projectRoot]);
|
|
35
|
+
// Keep ref in sync with requests length
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
requestsLengthRef.current = requests.length;
|
|
38
|
+
}, [requests.length]);
|
|
39
|
+
// Fetch request summaries from daemon
|
|
29
40
|
const fetchRequests = useCallback(async () => {
|
|
30
41
|
const client = clientRef.current;
|
|
31
42
|
if (!client) {
|
|
32
43
|
return;
|
|
33
44
|
}
|
|
45
|
+
const currentFilter = filterRef.current;
|
|
34
46
|
try {
|
|
35
47
|
// First check the count to avoid unnecessary data transfer
|
|
36
|
-
const count = await client.countRequests({
|
|
37
|
-
// Only fetch
|
|
38
|
-
if (count !== lastCountRef.current ||
|
|
39
|
-
const newRequests = await client.
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
const count = await client.countRequests({ filter: currentFilter });
|
|
49
|
+
// Only fetch list if count changed or we have no requests yet
|
|
50
|
+
if (count !== lastCountRef.current || requestsLengthRef.current === 0) {
|
|
51
|
+
const newRequests = await client.listRequestsSummary({
|
|
52
|
+
limit: DEFAULT_QUERY_LIMIT,
|
|
53
|
+
filter: currentFilter,
|
|
42
54
|
});
|
|
43
55
|
setRequests(newRequests);
|
|
44
56
|
lastCountRef.current = count;
|
|
@@ -48,7 +60,7 @@ export function useRequests(options = {}) {
|
|
|
48
60
|
catch (err) {
|
|
49
61
|
const message = err instanceof Error ? err.message : "Failed to connect to daemon";
|
|
50
62
|
if (message.includes("ENOENT") || message.includes("ECONNREFUSED")) {
|
|
51
|
-
setError("Daemon not running. Start with 'htpx
|
|
63
|
+
setError("Daemon not running. Start with 'eval $(htpx on)'.");
|
|
52
64
|
}
|
|
53
65
|
else {
|
|
54
66
|
setError(message);
|
|
@@ -57,13 +69,45 @@ export function useRequests(options = {}) {
|
|
|
57
69
|
finally {
|
|
58
70
|
setIsLoading(false);
|
|
59
71
|
}
|
|
60
|
-
}, [
|
|
72
|
+
}, []);
|
|
73
|
+
// Keep filter ref in sync and fetch immediately when filter changes
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
filterRef.current = filter;
|
|
76
|
+
lastCountRef.current = 0;
|
|
77
|
+
void fetchRequests();
|
|
78
|
+
}, [filter, fetchRequests]);
|
|
61
79
|
// Manual refresh function
|
|
62
80
|
const refresh = useCallback(async () => {
|
|
63
81
|
setIsLoading(true);
|
|
64
82
|
lastCountRef.current = 0; // Force full refresh
|
|
65
83
|
await fetchRequests();
|
|
66
84
|
}, [fetchRequests]);
|
|
85
|
+
// Fetch full request data by ID
|
|
86
|
+
const getFullRequest = useCallback(async (id) => {
|
|
87
|
+
const client = clientRef.current;
|
|
88
|
+
if (!client) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
return await client.getRequest(id);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}, []);
|
|
98
|
+
// Fetch all requests with full data (for exports like HAR)
|
|
99
|
+
const getAllFullRequests = useCallback(async () => {
|
|
100
|
+
const client = clientRef.current;
|
|
101
|
+
if (!client) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await client.listRequests({ limit: DEFAULT_QUERY_LIMIT });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}, []);
|
|
67
111
|
// Initial fetch
|
|
68
112
|
useEffect(() => {
|
|
69
113
|
void fetchRequests();
|
|
@@ -80,6 +124,8 @@ export function useRequests(options = {}) {
|
|
|
80
124
|
isLoading,
|
|
81
125
|
error,
|
|
82
126
|
refresh,
|
|
127
|
+
getFullRequest,
|
|
128
|
+
getAllFullRequests,
|
|
83
129
|
};
|
|
84
130
|
}
|
|
85
131
|
//# sourceMappingURL=useRequests.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRequests.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useRequests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useRequests.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useRequests.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for saving binary content to disk.
|
|
3
|
+
*/
|
|
4
|
+
import type { SaveLocation } from "../components/SaveModal.js";
|
|
5
|
+
export interface SaveResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
message: string;
|
|
8
|
+
filePath?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generate a filename for binary content based on request metadata.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateFilename(requestId: string, contentType: string | undefined, url: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Save binary content to disk.
|
|
16
|
+
*/
|
|
17
|
+
export declare function saveBinaryContent(body: Buffer, filename: string, location: SaveLocation, customPath?: string): Promise<SaveResult>;
|
|
18
|
+
interface UseSaveBinaryResult {
|
|
19
|
+
saveBinary: (body: Buffer, requestId: string, contentType: string | undefined, url: string, location: SaveLocation, customPath?: string) => Promise<SaveResult>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Hook providing binary save functionality.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useSaveBinary(): UseSaveBinaryResult;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=useSaveBinary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSaveBinary.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useSaveBinary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,WAAW,UAAU;IACzB,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,CAwBR;AAkGD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CA6BrB;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,CACV,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,UAAU,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,mBAAmB,CAiBnD"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for saving binary 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 binary 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
|
+
// Derive extension from content type
|
|
28
|
+
extension = getExtensionFromContentType(contentType);
|
|
29
|
+
}
|
|
30
|
+
// Generate timestamp for uniqueness
|
|
31
|
+
const timestamp = Date.now();
|
|
32
|
+
const shortId = requestId.slice(0, 8);
|
|
33
|
+
return extension ? `${shortId}-${timestamp}.${extension}` : `${shortId}-${timestamp}.bin`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Map common content types to file extensions.
|
|
37
|
+
*/
|
|
38
|
+
function getExtensionFromContentType(contentType) {
|
|
39
|
+
const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
40
|
+
const mappings = {
|
|
41
|
+
// Images
|
|
42
|
+
"image/jpeg": "jpg",
|
|
43
|
+
"image/png": "png",
|
|
44
|
+
"image/gif": "gif",
|
|
45
|
+
"image/webp": "webp",
|
|
46
|
+
"image/svg+xml": "svg",
|
|
47
|
+
"image/x-icon": "ico",
|
|
48
|
+
"image/bmp": "bmp",
|
|
49
|
+
"image/tiff": "tiff",
|
|
50
|
+
// Audio
|
|
51
|
+
"audio/mpeg": "mp3",
|
|
52
|
+
"audio/wav": "wav",
|
|
53
|
+
"audio/ogg": "ogg",
|
|
54
|
+
"audio/webm": "weba",
|
|
55
|
+
"audio/aac": "aac",
|
|
56
|
+
"audio/flac": "flac",
|
|
57
|
+
// Video
|
|
58
|
+
"video/mp4": "mp4",
|
|
59
|
+
"video/webm": "webm",
|
|
60
|
+
"video/ogg": "ogv",
|
|
61
|
+
"video/quicktime": "mov",
|
|
62
|
+
"video/x-msvideo": "avi",
|
|
63
|
+
"video/x-matroska": "mkv",
|
|
64
|
+
// Documents
|
|
65
|
+
"application/pdf": "pdf",
|
|
66
|
+
"application/msword": "doc",
|
|
67
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
68
|
+
"application/vnd.ms-excel": "xls",
|
|
69
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
70
|
+
"application/vnd.ms-powerpoint": "ppt",
|
|
71
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
|
|
72
|
+
// Archives
|
|
73
|
+
"application/zip": "zip",
|
|
74
|
+
"application/gzip": "gz",
|
|
75
|
+
"application/x-tar": "tar",
|
|
76
|
+
"application/x-rar-compressed": "rar",
|
|
77
|
+
"application/x-7z-compressed": "7z",
|
|
78
|
+
// Other
|
|
79
|
+
"application/octet-stream": "bin",
|
|
80
|
+
"application/wasm": "wasm",
|
|
81
|
+
};
|
|
82
|
+
return mappings[type] ?? "bin";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the target directory based on save location.
|
|
86
|
+
*/
|
|
87
|
+
function resolveTargetDir(location, customPath) {
|
|
88
|
+
switch (location) {
|
|
89
|
+
case "exports": {
|
|
90
|
+
const projectRoot = findOrCreateProjectRoot();
|
|
91
|
+
const htpxDir = ensureHtpxDir(projectRoot);
|
|
92
|
+
const exportsDir = path.join(htpxDir, "exports");
|
|
93
|
+
if (!fs.existsSync(exportsDir)) {
|
|
94
|
+
fs.mkdirSync(exportsDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
return exportsDir;
|
|
97
|
+
}
|
|
98
|
+
case "downloads": {
|
|
99
|
+
const downloadsDir = path.join(os.homedir(), "Downloads");
|
|
100
|
+
if (!fs.existsSync(downloadsDir)) {
|
|
101
|
+
fs.mkdirSync(downloadsDir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
return downloadsDir;
|
|
104
|
+
}
|
|
105
|
+
case "custom": {
|
|
106
|
+
if (!customPath) {
|
|
107
|
+
throw new Error("Custom path required");
|
|
108
|
+
}
|
|
109
|
+
// Expand ~ to home directory
|
|
110
|
+
const expandedPath = customPath.startsWith("~")
|
|
111
|
+
? path.join(os.homedir(), customPath.slice(1))
|
|
112
|
+
: customPath;
|
|
113
|
+
const resolvedPath = path.resolve(expandedPath);
|
|
114
|
+
// Ensure directory exists
|
|
115
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
116
|
+
fs.mkdirSync(resolvedPath, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
return resolvedPath;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Save binary content to disk.
|
|
124
|
+
*/
|
|
125
|
+
export async function saveBinaryContent(body, filename, location, customPath) {
|
|
126
|
+
try {
|
|
127
|
+
const targetDir = resolveTargetDir(location, customPath);
|
|
128
|
+
const filePath = path.join(targetDir, filename);
|
|
129
|
+
fs.writeFileSync(filePath, body);
|
|
130
|
+
// Copy path to clipboard
|
|
131
|
+
try {
|
|
132
|
+
await copyToClipboard(filePath);
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
message: `Saved to ${filePath} (path copied)`,
|
|
136
|
+
filePath,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Clipboard failed but file was saved
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
message: `Saved to ${filePath}`,
|
|
144
|
+
filePath,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
message: err instanceof Error ? err.message : "Failed to save file",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Hook providing binary save functionality.
|
|
157
|
+
*/
|
|
158
|
+
export function useSaveBinary() {
|
|
159
|
+
const saveBinary = useCallback(async (body, requestId, contentType, url, location, customPath) => {
|
|
160
|
+
const filename = generateFilename(requestId, contentType, url);
|
|
161
|
+
return saveBinaryContent(body, filename, location, customPath);
|
|
162
|
+
}, []);
|
|
163
|
+
return { saveBinary };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=useSaveBinary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSaveBinary.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useSaveBinary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSpinner.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useSpinner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,wBAAgB,UAAU,CAAC,MAAM,UAAO,GAAG,MAAM,CAqBhD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook that cycles through braille spinner frames for loading indicators.
|
|
3
|
+
*/
|
|
4
|
+
import { useState, useEffect, useRef } from "react";
|
|
5
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
6
|
+
const SPINNER_INTERVAL_MS = 80;
|
|
7
|
+
export function useSpinner(active = true) {
|
|
8
|
+
const [frameIndex, setFrameIndex] = useState(0);
|
|
9
|
+
const intervalRef = useRef(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!active) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
intervalRef.current = setInterval(() => {
|
|
15
|
+
setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
16
|
+
}, SPINNER_INTERVAL_MS);
|
|
17
|
+
return () => {
|
|
18
|
+
if (intervalRef.current) {
|
|
19
|
+
clearInterval(intervalRef.current);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}, [active]);
|
|
23
|
+
return SPINNER_FRAMES[frameIndex] ?? SPINNER_FRAMES[0];
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=useSpinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSpinner.js","sourceRoot":"","sources":["../../../../src/cli/tui/hooks/useSpinner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEpD,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AACnF,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,MAAM,UAAU,UAAU,CAAC,MAAM,GAAG,IAAI;IACtC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QAED,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAExB,OAAO,GAAG,EAAE;YACV,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,cAAc,CAAC,UAAU,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary content detection utilities.
|
|
3
|
+
*
|
|
4
|
+
* Determines whether a buffer contains binary or text content based on
|
|
5
|
+
* Content-Type headers and byte scanning.
|
|
6
|
+
*/
|
|
7
|
+
export interface BinaryCheckResult {
|
|
8
|
+
isBinary: boolean;
|
|
9
|
+
reason: "content-type" | "content-scan" | "text-content-type";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Check if content is binary based on Content-Type and/or byte scanning.
|
|
13
|
+
*
|
|
14
|
+
* Detection logic:
|
|
15
|
+
* 1. Check Content-Type against text whitelist - if match, return not binary
|
|
16
|
+
* 2. Check Content-Type against binary patterns - if match, return binary
|
|
17
|
+
* 3. Fall back to scanning first 8KB for >10% non-printable characters
|
|
18
|
+
*/
|
|
19
|
+
export declare function isBinaryContent(body: Buffer | undefined, contentType: string | undefined): BinaryCheckResult;
|
|
20
|
+
/**
|
|
21
|
+
* Get a human-readable description for a binary content type.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getBinaryTypeDescription(contentType: string | undefined): string;
|
|
24
|
+
//# sourceMappingURL=binary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/utils/binary.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4CH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,mBAAmB,CAAC;CAC/D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,iBAAiB,CAiDnB;AAoCD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CA2BhF"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary content detection utilities.
|
|
3
|
+
*
|
|
4
|
+
* Determines whether a buffer contains binary or text content based on
|
|
5
|
+
* Content-Type headers and byte scanning.
|
|
6
|
+
*/
|
|
7
|
+
import { TEXT_CONTENT_TYPES, TEXT_SUFFIXES } from "../../../shared/content-type.js";
|
|
8
|
+
/**
|
|
9
|
+
* Content types that are definitively binary.
|
|
10
|
+
*/
|
|
11
|
+
const BINARY_CONTENT_TYPES = [
|
|
12
|
+
"image/",
|
|
13
|
+
"audio/",
|
|
14
|
+
"video/",
|
|
15
|
+
"application/octet-stream",
|
|
16
|
+
"application/pdf",
|
|
17
|
+
"application/zip",
|
|
18
|
+
"application/gzip",
|
|
19
|
+
"application/x-tar",
|
|
20
|
+
"application/x-rar-compressed",
|
|
21
|
+
"application/x-7z-compressed",
|
|
22
|
+
"application/vnd.ms-excel",
|
|
23
|
+
"application/vnd.ms-powerpoint",
|
|
24
|
+
"application/vnd.ms-word",
|
|
25
|
+
"application/vnd.openxmlformats-officedocument",
|
|
26
|
+
"application/x-shockwave-flash",
|
|
27
|
+
"application/wasm",
|
|
28
|
+
"font/",
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Human-readable descriptions for binary content types.
|
|
32
|
+
*/
|
|
33
|
+
const BINARY_DESCRIPTIONS = {
|
|
34
|
+
"image/": "Image",
|
|
35
|
+
"audio/": "Audio",
|
|
36
|
+
"video/": "Video",
|
|
37
|
+
"font/": "Font",
|
|
38
|
+
"application/pdf": "PDF",
|
|
39
|
+
"application/zip": "Archive",
|
|
40
|
+
"application/gzip": "Archive",
|
|
41
|
+
"application/x-tar": "Archive",
|
|
42
|
+
"application/x-rar-compressed": "Archive",
|
|
43
|
+
"application/x-7z-compressed": "Archive",
|
|
44
|
+
"application/wasm": "WebAssembly",
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Check if content is binary based on Content-Type and/or byte scanning.
|
|
48
|
+
*
|
|
49
|
+
* Detection logic:
|
|
50
|
+
* 1. Check Content-Type against text whitelist - if match, return not binary
|
|
51
|
+
* 2. Check Content-Type against binary patterns - if match, return binary
|
|
52
|
+
* 3. Fall back to scanning first 8KB for >10% non-printable characters
|
|
53
|
+
*/
|
|
54
|
+
export function isBinaryContent(body, contentType) {
|
|
55
|
+
// No body means not binary (nothing to display)
|
|
56
|
+
if (!body || body.length === 0) {
|
|
57
|
+
return { isBinary: false, reason: "text-content-type" };
|
|
58
|
+
}
|
|
59
|
+
// Normalise content type (remove charset and other parameters)
|
|
60
|
+
const normalisedType = contentType?.split(";")[0]?.trim().toLowerCase();
|
|
61
|
+
// Check text content types first
|
|
62
|
+
if (normalisedType) {
|
|
63
|
+
// Check prefixes
|
|
64
|
+
let isTextContentType = false;
|
|
65
|
+
for (const prefix of TEXT_CONTENT_TYPES) {
|
|
66
|
+
if (normalisedType.startsWith(prefix)) {
|
|
67
|
+
isTextContentType = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Check suffixes (e.g., application/hal+json)
|
|
72
|
+
if (!isTextContentType) {
|
|
73
|
+
for (const suffix of TEXT_SUFFIXES) {
|
|
74
|
+
if (normalisedType.endsWith(suffix)) {
|
|
75
|
+
isTextContentType = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (isTextContentType) {
|
|
81
|
+
// Content-type says text, but verify with byte scan as safety net.
|
|
82
|
+
// Handles compressed bodies still in the DB, mislabelled content types, etc.
|
|
83
|
+
const scanResult = scanForBinaryContent(body);
|
|
84
|
+
return scanResult.isBinary
|
|
85
|
+
? { isBinary: true, reason: "content-scan" }
|
|
86
|
+
: { isBinary: false, reason: "text-content-type" };
|
|
87
|
+
}
|
|
88
|
+
// Check binary content types
|
|
89
|
+
for (const prefix of BINARY_CONTENT_TYPES) {
|
|
90
|
+
if (normalisedType.startsWith(prefix)) {
|
|
91
|
+
return { isBinary: true, reason: "content-type" };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Fall back to byte scanning
|
|
96
|
+
return scanForBinaryContent(body);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Scan buffer for binary content by checking for non-printable characters.
|
|
100
|
+
* Scans first 8KB and considers binary if >10% are non-printable.
|
|
101
|
+
*/
|
|
102
|
+
function scanForBinaryContent(body) {
|
|
103
|
+
const SCAN_SIZE = 8 * 1024; // 8KB
|
|
104
|
+
const BINARY_THRESHOLD = 0.1; // 10%
|
|
105
|
+
const bytesToScan = Math.min(body.length, SCAN_SIZE);
|
|
106
|
+
let nonPrintableCount = 0;
|
|
107
|
+
for (let i = 0; i < bytesToScan; i++) {
|
|
108
|
+
const byte = body[i];
|
|
109
|
+
if (byte === undefined)
|
|
110
|
+
continue;
|
|
111
|
+
// Allow common text characters:
|
|
112
|
+
// - Tab (9), LF (10), CR (13)
|
|
113
|
+
// - Printable ASCII (32-126)
|
|
114
|
+
// - Extended ASCII (128-255) for UTF-8
|
|
115
|
+
const isPrintable = byte === 9 || byte === 10 || byte === 13 || (byte >= 32 && byte <= 126) || byte >= 128;
|
|
116
|
+
if (!isPrintable) {
|
|
117
|
+
nonPrintableCount++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const ratio = nonPrintableCount / bytesToScan;
|
|
121
|
+
return {
|
|
122
|
+
isBinary: ratio > BINARY_THRESHOLD,
|
|
123
|
+
reason: "content-scan",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get a human-readable description for a binary content type.
|
|
128
|
+
*/
|
|
129
|
+
export function getBinaryTypeDescription(contentType) {
|
|
130
|
+
if (!contentType) {
|
|
131
|
+
return "Binary";
|
|
132
|
+
}
|
|
133
|
+
const normalisedType = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
134
|
+
// Check exact matches first
|
|
135
|
+
if (BINARY_DESCRIPTIONS[normalisedType]) {
|
|
136
|
+
return BINARY_DESCRIPTIONS[normalisedType];
|
|
137
|
+
}
|
|
138
|
+
// Check prefix matches
|
|
139
|
+
for (const [prefix, description] of Object.entries(BINARY_DESCRIPTIONS)) {
|
|
140
|
+
if (prefix.endsWith("/") && normalisedType.startsWith(prefix)) {
|
|
141
|
+
return description;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Extract subtype for better description
|
|
145
|
+
if (normalisedType.startsWith("application/")) {
|
|
146
|
+
const subtype = normalisedType.replace("application/", "");
|
|
147
|
+
// Capitalise first letter
|
|
148
|
+
return subtype.charAt(0).toUpperCase() + subtype.slice(1);
|
|
149
|
+
}
|
|
150
|
+
return "Binary";
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=binary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.js","sourceRoot":"","sources":["../../../../src/cli/tui/utils/binary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform clipboard utilities using native commands.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Copy text to the system clipboard.
|
|
6
|
+
* Uses pbcopy on macOS, xclip on Linux, clip on Windows.
|
|
7
|
+
*/
|
|
8
|
+
export declare function copyToClipboard(text: string): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/utils/clipboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDjE"}
|