@buoy-gg/network 7.0.12 → 7.0.14
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/lib/commonjs/index.js +52 -0
- package/lib/commonjs/network/components/NetworkDetailStepper.js +26 -14
- package/lib/commonjs/network/components/NetworkEventDetailView.js +86 -14
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +4 -0
- package/lib/commonjs/network/components/NetworkHeaderLive.js +32 -7
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +183 -0
- package/lib/commonjs/network/components/NetworkListScreen.js +134 -8
- package/lib/commonjs/network/components/NetworkModal.js +198 -51
- package/lib/commonjs/network/components/NetworkNumberPrompt.js +178 -0
- package/lib/commonjs/network/components/NetworkOverrideEditor.js +797 -0
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.js +102 -0
- package/lib/commonjs/network/components/NetworkOverridesButton.js +88 -0
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +489 -0
- package/lib/commonjs/network/components/response-editor/Explorer.js +973 -0
- package/lib/commonjs/network/components/response-editor/deleteNestedDataByPath.js +56 -0
- package/lib/commonjs/network/components/response-editor/editorContext.js +33 -0
- package/lib/commonjs/network/components/response-editor/updateNestedDataByPath.js +61 -0
- package/lib/commonjs/network/hooks/useNetworkSelection.js +53 -0
- package/lib/commonjs/network/hooks/useOverrideRules.js +36 -0
- package/lib/commonjs/network/overrides/applyToFetch.js +80 -0
- package/lib/commonjs/network/overrides/applyToXhr.js +190 -0
- package/lib/commonjs/network/overrides/engine.js +70 -0
- package/lib/commonjs/network/overrides/matchRule.js +81 -0
- package/lib/commonjs/network/overrides/overrideRulesStore.js +550 -0
- package/lib/commonjs/network/overrides/presets.js +70 -0
- package/lib/commonjs/network/overrides/resolveOverride.js +158 -0
- package/lib/commonjs/network/overrides/types.js +85 -0
- package/lib/commonjs/network/sync/networkSyncAdapter.js +230 -1
- package/lib/commonjs/network/types/index.js +5 -1
- package/lib/commonjs/network/utils/networkEventStore.js +52 -6
- package/lib/commonjs/network/utils/networkListener.js +90 -10
- package/lib/commonjs/network/utils/networkSelectionStore.js +193 -0
- package/lib/commonjs/preset.js +11 -0
- package/lib/module/index.js +19 -0
- package/lib/module/network/components/NetworkDetailStepper.js +27 -15
- package/lib/module/network/components/NetworkEventDetailView.js +87 -15
- package/lib/module/network/components/NetworkEventItemCompact.js +5 -1
- package/lib/module/network/components/NetworkHeaderLive.js +32 -7
- package/lib/module/network/components/NetworkHeaderMenu.js +177 -0
- package/lib/module/network/components/NetworkListScreen.js +135 -9
- package/lib/module/network/components/NetworkModal.js +200 -53
- package/lib/module/network/components/NetworkNumberPrompt.js +174 -0
- package/lib/module/network/components/NetworkOverrideEditor.js +794 -0
- package/lib/module/network/components/NetworkOverrideHeaderButton.js +99 -0
- package/lib/module/network/components/NetworkOverridesButton.js +85 -0
- package/lib/module/network/components/NetworkOverridesScreen.js +485 -0
- package/lib/module/network/components/response-editor/Explorer.js +972 -0
- package/lib/module/network/components/response-editor/deleteNestedDataByPath.js +51 -0
- package/lib/module/network/components/response-editor/editorContext.js +29 -0
- package/lib/module/network/components/response-editor/updateNestedDataByPath.js +56 -0
- package/lib/module/network/hooks/useNetworkSelection.js +46 -0
- package/lib/module/network/hooks/useOverrideRules.js +32 -0
- package/lib/module/network/overrides/applyToFetch.js +72 -0
- package/lib/module/network/overrides/applyToXhr.js +185 -0
- package/lib/module/network/overrides/engine.js +64 -0
- package/lib/module/network/overrides/matchRule.js +75 -0
- package/lib/module/network/overrides/overrideRulesStore.js +547 -0
- package/lib/module/network/overrides/presets.js +65 -0
- package/lib/module/network/overrides/resolveOverride.js +152 -0
- package/lib/module/network/overrides/types.js +80 -0
- package/lib/module/network/sync/networkSyncAdapter.js +230 -1
- package/lib/module/network/types/index.js +3 -1
- package/lib/module/network/utils/networkEventStore.js +52 -6
- package/lib/module/network/utils/networkListener.js +91 -10
- package/lib/module/network/utils/networkSelectionStore.js +188 -0
- package/lib/module/preset.js +13 -1
- package/lib/typescript/index.d.ts +9 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts +8 -5
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts +36 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts +6 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts +13 -3
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts +46 -0
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts +33 -0
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts +17 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts +27 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts +17 -0
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts +29 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/Explorer.d.ts +17 -0
- package/lib/typescript/network/components/response-editor/Explorer.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts +9 -0
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/editorContext.d.ts +29 -0
- package/lib/typescript/network/components/response-editor/editorContext.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts +10 -0
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts +26 -0
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useOverrideRules.d.ts +29 -0
- package/lib/typescript/network/hooks/useOverrideRules.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/applyToFetch.test.d.ts +16 -0
- package/lib/typescript/network/overrides/__tests__/applyToFetch.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts +19 -0
- package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/resolveOverride.test.d.ts +22 -0
- package/lib/typescript/network/overrides/__tests__/resolveOverride.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/applyToFetch.d.ts +50 -0
- package/lib/typescript/network/overrides/applyToFetch.d.ts.map +1 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts +43 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -0
- package/lib/typescript/network/overrides/engine.d.ts +47 -0
- package/lib/typescript/network/overrides/engine.d.ts.map +1 -0
- package/lib/typescript/network/overrides/matchRule.d.ts +22 -0
- package/lib/typescript/network/overrides/matchRule.d.ts.map +1 -0
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts +205 -0
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts.map +1 -0
- package/lib/typescript/network/overrides/presets.d.ts +38 -0
- package/lib/typescript/network/overrides/presets.d.ts.map +1 -0
- package/lib/typescript/network/overrides/resolveOverride.d.ts +46 -0
- package/lib/typescript/network/overrides/resolveOverride.d.ts.map +1 -0
- package/lib/typescript/network/overrides/types.d.ts +169 -0
- package/lib/typescript/network/overrides/types.d.ts.map +1 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +73 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/network/types/index.d.ts +10 -0
- package/lib/typescript/network/types/index.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts +14 -0
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkListener.d.ts +3 -0
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkSelectionStore.d.ts +117 -0
- package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -2,18 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useEffect, useCallback } from "react";
|
|
4
4
|
import { View, StyleSheet, TouchableOpacity, TextInput } from "react-native";
|
|
5
|
-
import { Search, Filter, X, JsModal, ModalHeader, devToolsStorageKeys, macOSColors, persistentStorage, TabSelector, useFeatureGate, UpgradeModal, PowerToggleButton, useIgnoredPatterns } from "@buoy-gg/shared-ui";
|
|
5
|
+
import { Search, Filter, Bookmark, X, JsModal, ModalHeader, devToolsStorageKeys, macOSColors, persistentStorage, TabSelector, useFeatureGate, UpgradeModal, PowerToggleButton, useIgnoredPatterns } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { NetworkFilterViewV3 } from "./NetworkFilterViewV3";
|
|
7
7
|
import { TickProvider } from "../hooks/useTickEveryMinute";
|
|
8
8
|
import { NetworkEventDetailView } from "./NetworkEventDetailView";
|
|
9
9
|
import { NetworkCopySettingsView, DEFAULT_COPY_SETTINGS } from "./NetworkCopySettingsView";
|
|
10
|
-
import { NetworkHeaderBadges, NetworkClearButton, NetworkDetailPinActions, NetworkHeaderCopyButton
|
|
10
|
+
import { NetworkHeaderBadges, NetworkClearButton, NetworkDetailPinActions, NetworkHeaderCopyButton } from "./NetworkHeaderLive";
|
|
11
11
|
import { NetworkListScreen } from "./NetworkListScreen";
|
|
12
12
|
import { NetworkSavedScreen } from "./NetworkSavedScreen";
|
|
13
|
+
import { NetworkOverridesScreen } from "./NetworkOverridesScreen";
|
|
14
|
+
import { NetworkOverridesButton } from "./NetworkOverridesButton";
|
|
15
|
+
import { NetworkOverrideHeaderButton } from "./NetworkOverrideHeaderButton";
|
|
16
|
+
import { NetworkHeaderMenu, NetworkHeaderMenuButton, NetworkHeaderMenuItem } from "./NetworkHeaderMenu";
|
|
13
17
|
import { NetworkDetailStepper } from "./NetworkDetailStepper";
|
|
14
18
|
import { useNetworkEventList } from "../hooks/useNetworkEventList";
|
|
15
19
|
import { useLiveNetworkEvent } from "../hooks/useLiveNetworkEvent";
|
|
20
|
+
import { useHasNetworkSelection, useNetworkBodyEventId, useSelectedNetworkEventId } from "../hooks/useNetworkSelection";
|
|
21
|
+
import { networkSelectionStore } from "../utils/networkSelectionStore";
|
|
16
22
|
import { FREE_TIER_SAVED_LIMIT, MAX_SAVED } from "../utils/networkSavedStore";
|
|
23
|
+
import { FREE_TIER_RULE_LIMIT as FREE_TIER_OVERRIDE_LIMIT, MAX_RULES as MAX_OVERRIDE_RULES } from "../overrides/types";
|
|
17
24
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
25
|
// ============================================================================
|
|
19
26
|
// Screens (rule4 stable router: each gates itself with `return null`; the
|
|
@@ -24,7 +31,6 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
24
31
|
|
|
25
32
|
function NetworkDetailScreen({
|
|
26
33
|
visible,
|
|
27
|
-
eventId,
|
|
28
34
|
paused,
|
|
29
35
|
ignoredPatternValues,
|
|
30
36
|
onTogglePattern,
|
|
@@ -33,34 +39,66 @@ function NetworkDetailScreen({
|
|
|
33
39
|
ignoredPatterns,
|
|
34
40
|
fromSaved,
|
|
35
41
|
savedSearch,
|
|
36
|
-
onSelectEvent,
|
|
37
42
|
applySafeAreaInset
|
|
38
43
|
}) {
|
|
44
|
+
/**
|
|
45
|
+
* The detail renders at the IMMEDIATE selection — every step, no delay.
|
|
46
|
+
* Measured, 100 steps at a 150ms tap interval over 64KB payloads:
|
|
47
|
+
*
|
|
48
|
+
* whole detail, bodies included ... 647ms/step
|
|
49
|
+
* detail with no bodies at all .... 43ms/step (over the 150ms tap gap)
|
|
50
|
+
*
|
|
51
|
+
* So ~93% of a step is the request/response BODY trees, and everything else
|
|
52
|
+
* — status, URL breakdown, timing, header counts — is affordable at any tap
|
|
53
|
+
* rate. Deferring the whole detail (which is what this used to do) therefore
|
|
54
|
+
* bought nothing a user could feel: it made the cheap 43ms wait alongside
|
|
55
|
+
* the expensive 450ms instead of splitting them.
|
|
56
|
+
*
|
|
57
|
+
* So only the expensive part waits. `bodyId` trails `selectedId` by the
|
|
58
|
+
* settle window, so `heavyReady` is false exactly while the cursor is still
|
|
59
|
+
* moving — the JSON trees are skipped during a burst and mount once you land.
|
|
60
|
+
*/
|
|
61
|
+
const eventId = useSelectedNetworkEventId();
|
|
62
|
+
const settledId = useNetworkBodyEventId();
|
|
63
|
+
const heavyReady = eventId != null && eventId === settledId;
|
|
39
64
|
// Resolve the ID to the live event so a request opened while pending picks
|
|
40
65
|
// up its response/error in place instead of freezing on the snapshot taken
|
|
41
66
|
// when the row was tapped.
|
|
42
67
|
const event = useLiveNetworkEvent(eventId, {
|
|
43
68
|
paused: paused || !visible
|
|
44
69
|
});
|
|
45
|
-
|
|
70
|
+
|
|
71
|
+
// Instrumentation for the benchmark harness. Gated on `heavyReady` on
|
|
72
|
+
// purpose: the cheap chrome now renders on every step, so reporting here
|
|
73
|
+
// unconditionally would make `bodyMs` a duplicate of `cursorMs`. What the
|
|
74
|
+
// harness needs to know is when the expensive TREES landed.
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (visible && event && heavyReady) {
|
|
77
|
+
networkSelectionStore.reportRendered(event.id);
|
|
78
|
+
}
|
|
79
|
+
}, [visible, event, heavyReady]);
|
|
80
|
+
if (!visible) return null;
|
|
46
81
|
// Column: the detail body scrolls, the stepper stays pinned under it. The
|
|
47
82
|
// body owns its scroll here (see `bodyOwnsScroll`) — inside JsModal's
|
|
48
|
-
// wrapper the footer would scroll away with the content.
|
|
83
|
+
// wrapper the footer would scroll away with the content. The spacer keeps
|
|
84
|
+
// the footer at the bottom on the rare frame where the ID resolves to
|
|
85
|
+
// nothing (cleared, aged out, and not saved).
|
|
49
86
|
return /*#__PURE__*/_jsxs(View, {
|
|
50
87
|
style: styles.detailRoot,
|
|
51
|
-
children: [/*#__PURE__*/_jsx(NetworkEventDetailView, {
|
|
88
|
+
children: [event ? /*#__PURE__*/_jsx(NetworkEventDetailView, {
|
|
52
89
|
event: event,
|
|
53
90
|
ignoredPatterns: ignoredPatternValues,
|
|
54
|
-
onTogglePattern: onTogglePattern
|
|
91
|
+
onTogglePattern: onTogglePattern,
|
|
92
|
+
showBodies: heavyReady
|
|
93
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
94
|
+
style: styles.detailBodyFallback
|
|
55
95
|
}), /*#__PURE__*/_jsx(NetworkDetailStepper, {
|
|
56
|
-
eventId: event.id,
|
|
57
96
|
paused: paused,
|
|
58
97
|
filter: filter,
|
|
59
98
|
ignoredPatterns: ignoredPatterns,
|
|
60
99
|
isPro: isPro,
|
|
61
100
|
fromSaved: fromSaved,
|
|
62
101
|
savedSearch: savedSearch,
|
|
63
|
-
onSelect: onSelectEvent,
|
|
64
102
|
applySafeAreaInset: applySafeAreaInset
|
|
65
103
|
})]
|
|
66
104
|
});
|
|
@@ -155,11 +193,38 @@ function NetworkModalInner({
|
|
|
155
193
|
if (controlledPause) onPausedChange(value);else setPausedState(value);
|
|
156
194
|
};
|
|
157
195
|
const [filter, setFilter] = useState({});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
/**
|
|
197
|
+
* The cursor lives in `networkSelectionStore`, and the root subscribes to
|
|
198
|
+
* the BOOLEAN only. That is the whole point: this component re-renders
|
|
199
|
+
* `JsModal` (which can never bail — its `header` prop is a fresh object
|
|
200
|
+
* every render), the entire header, and all five screens. None of them care
|
|
201
|
+
* WHICH request is selected, only THAT one is, and this flips twice a
|
|
202
|
+
* session instead of once per tap.
|
|
203
|
+
*/
|
|
204
|
+
const hasSelection = useHasNetworkSelection();
|
|
161
205
|
const [showFilterView, setShowFilterView] = useState(false);
|
|
162
206
|
const [showSavedView, setShowSavedView] = useState(false);
|
|
207
|
+
const [showOverridesView, setShowOverridesView] = useState(false);
|
|
208
|
+
const [showHeaderMenu, setShowHeaderMenu] = useState(false);
|
|
209
|
+
/**
|
|
210
|
+
* Overrides are a DEVICE capability — a rule runs inside the app's own
|
|
211
|
+
* `fetch`/`XMLHttpRequest`, which the dashboard doesn't have.
|
|
212
|
+
*
|
|
213
|
+
* That doesn't mean the dashboard can't AUTHOR them: in `inline` mode the
|
|
214
|
+
* rule store runs as a mirror of the connected device (see
|
|
215
|
+
* `overrideRulesStore.enableRemoteMode`), so the screens below are the same
|
|
216
|
+
* ones the phone renders and every edit is forwarded. The one thing the
|
|
217
|
+
* dashboard never does is apply a rule to its own traffic —
|
|
218
|
+
* `disableEngine()` guarantees that.
|
|
219
|
+
*/
|
|
220
|
+
const overridesAvailable = true;
|
|
221
|
+
/**
|
|
222
|
+
* A rule prefilled from a request the user was looking at, handed to the
|
|
223
|
+
* Overrides screen when it mounts. Lives here rather than in that screen
|
|
224
|
+
* because the request that seeds it is on a DIFFERENT screen — this is the
|
|
225
|
+
* one place both are in scope.
|
|
226
|
+
*/
|
|
227
|
+
const [pendingOverrideDraft, setPendingOverrideDraft] = useState(null);
|
|
163
228
|
const [activeTab, setActiveTab] = useState("filters");
|
|
164
229
|
const [searchText, setSearchText] = useState("");
|
|
165
230
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
@@ -231,13 +296,32 @@ function NetworkModalInner({
|
|
|
231
296
|
|
|
232
297
|
// Simple handlers - no useCallback needed per rule2
|
|
233
298
|
const handleBack = () => {
|
|
234
|
-
|
|
299
|
+
networkSelectionStore.clear();
|
|
235
300
|
};
|
|
236
301
|
|
|
237
302
|
// Stable identity: this is passed down to the memoized list rows.
|
|
238
303
|
const handleEventPress = useCallback(event => {
|
|
239
|
-
|
|
304
|
+
networkSelectionStore.select(event.id);
|
|
305
|
+
}, []);
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* "Custom…" in a request's Override section. Stable identity because the
|
|
309
|
+
* detail view is memoized on its props and an unstable callback would defeat
|
|
310
|
+
* the bail-out that keeps prev/next stepping cheap.
|
|
311
|
+
*/
|
|
312
|
+
const handleOverridePress = useCallback((draft, ruleId) => {
|
|
313
|
+
setPendingOverrideDraft(ruleId ? {
|
|
314
|
+
...draft,
|
|
315
|
+
id: ruleId
|
|
316
|
+
} : draft);
|
|
317
|
+
setShowOverridesView(true);
|
|
318
|
+
// Leave the detail view, or its header would sit on top of the editor.
|
|
319
|
+
networkSelectionStore.clear();
|
|
240
320
|
}, []);
|
|
321
|
+
|
|
322
|
+
// Stable identity: the Overrides screen adopts the draft in an effect keyed
|
|
323
|
+
// on both, so an unstable callback would re-run it every render.
|
|
324
|
+
const handlePendingOverrideConsumed = useCallback(() => setPendingOverrideDraft(null), []);
|
|
241
325
|
const handleSearch = text => {
|
|
242
326
|
setSearchText(text);
|
|
243
327
|
setFilter(prev => ({
|
|
@@ -294,19 +378,35 @@ function NetworkModalInner({
|
|
|
294
378
|
}
|
|
295
379
|
|
|
296
380
|
// Event detail view header
|
|
297
|
-
if (
|
|
381
|
+
if (hasSelection) {
|
|
298
382
|
return /*#__PURE__*/_jsxs(ModalHeader, {
|
|
299
383
|
children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
300
384
|
onBack: handleBack
|
|
301
385
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
302
386
|
title: "Request Details",
|
|
303
387
|
centered: true
|
|
304
|
-
}), /*#__PURE__*/
|
|
305
|
-
children: /*#__PURE__*/_jsx(
|
|
306
|
-
|
|
388
|
+
}), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
|
|
389
|
+
children: [overridesAvailable ? /*#__PURE__*/_jsx(NetworkOverrideHeaderButton, {
|
|
390
|
+
onOpen: handleOverridePress
|
|
391
|
+
}) : null, /*#__PURE__*/_jsx(NetworkDetailPinActions, {
|
|
307
392
|
isPro: isPro,
|
|
308
393
|
onUpgradePress: () => showUpgrade("saved")
|
|
309
|
-
})
|
|
394
|
+
})]
|
|
395
|
+
})]
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Overrides manager header.
|
|
400
|
+
if (showOverridesView && overridesAvailable) {
|
|
401
|
+
return /*#__PURE__*/_jsxs(ModalHeader, {
|
|
402
|
+
children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
403
|
+
onBack: () => {
|
|
404
|
+
setShowOverridesView(false);
|
|
405
|
+
setPendingOverrideDraft(null);
|
|
406
|
+
}
|
|
407
|
+
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
408
|
+
title: "Response Overrides",
|
|
409
|
+
centered: true
|
|
310
410
|
})]
|
|
311
411
|
});
|
|
312
412
|
}
|
|
@@ -378,35 +478,26 @@ function NetworkModalInner({
|
|
|
378
478
|
size: 14,
|
|
379
479
|
color: macOSColors.text.secondary
|
|
380
480
|
})
|
|
381
|
-
}), /*#__PURE__*/_jsx(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
setShowFilterView(true);
|
|
385
|
-
},
|
|
386
|
-
style: [styles.headerActionButton, (filter.status || filter.method || filter.contentType) && styles.activeFilterButton],
|
|
387
|
-
children: /*#__PURE__*/_jsx(Filter, {
|
|
388
|
-
size: 14,
|
|
389
|
-
color: filter.status || filter.method || filter.contentType ? macOSColors.semantic.info : macOSColors.text.muted
|
|
390
|
-
})
|
|
391
|
-
}), /*#__PURE__*/_jsx(NetworkSavedButton, {
|
|
392
|
-
onPress: () => setShowSavedView(true)
|
|
393
|
-
}), /*#__PURE__*/_jsx(NetworkHeaderCopyButton, {
|
|
394
|
-
isPro: isPro,
|
|
395
|
-
filter: filter,
|
|
396
|
-
ignoredPatterns: ignoredPatterns,
|
|
397
|
-
copySettings: copySettings,
|
|
398
|
-
paused: paused,
|
|
399
|
-
onUpgradePress: () => showUpgrade("export")
|
|
400
|
-
}), /*#__PURE__*/_jsx(PowerToggleButton, {
|
|
481
|
+
}), overridesAvailable ? /*#__PURE__*/_jsx(NetworkOverridesButton, {
|
|
482
|
+
onPress: () => setShowOverridesView(true)
|
|
483
|
+
}) : null, /*#__PURE__*/_jsx(PowerToggleButton, {
|
|
401
484
|
isEnabled: !paused,
|
|
402
485
|
onToggle: () => setPaused(prev => !prev),
|
|
403
486
|
accessibilityLabel: "Toggle network interception"
|
|
404
487
|
}), /*#__PURE__*/_jsx(NetworkClearButton, {
|
|
405
488
|
paused: paused
|
|
489
|
+
}), /*#__PURE__*/_jsx(NetworkHeaderMenuButton, {
|
|
490
|
+
onPress: () => setShowHeaderMenu(prev => !prev),
|
|
491
|
+
isOpen: showHeaderMenu,
|
|
492
|
+
hasIndicator: hasActiveFilter
|
|
406
493
|
})]
|
|
407
494
|
})]
|
|
408
495
|
});
|
|
409
496
|
};
|
|
497
|
+
|
|
498
|
+
// Surfaced on the ⋯ so moving Filters into the menu doesn't hide the one
|
|
499
|
+
// thing that button was communicating.
|
|
500
|
+
const hasActiveFilter = !!(filter.status || filter.method || filter.contentType);
|
|
410
501
|
const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.network.modal();
|
|
411
502
|
if (!visible) return null;
|
|
412
503
|
|
|
@@ -417,13 +508,12 @@ function NetworkModalInner({
|
|
|
417
508
|
// same deal. The DETAIL screen owns it as well, so its prev/next footer
|
|
418
509
|
// stays pinned instead of scrolling away with the body. Filter/copy still
|
|
419
510
|
// use the wrapper.
|
|
420
|
-
const bodyOwnsScroll =
|
|
511
|
+
const bodyOwnsScroll = hasSelection || !showFilterView;
|
|
421
512
|
const body = /*#__PURE__*/_jsxs(_Fragment, {
|
|
422
513
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
423
514
|
style: styles.container,
|
|
424
515
|
children: [/*#__PURE__*/_jsx(NetworkDetailScreen, {
|
|
425
|
-
visible:
|
|
426
|
-
eventId: selectedEventId,
|
|
516
|
+
visible: hasSelection,
|
|
427
517
|
paused: paused,
|
|
428
518
|
ignoredPatternValues: ignoredPatternValues,
|
|
429
519
|
onTogglePattern: toggleIgnoredPattern,
|
|
@@ -434,13 +524,12 @@ function NetworkModalInner({
|
|
|
434
524
|
// the user came from — and therefore which one to step through.
|
|
435
525
|
,
|
|
436
526
|
fromSaved: showSavedView,
|
|
437
|
-
savedSearch: savedSearch
|
|
438
|
-
onSelectEvent: setSelectedEventId
|
|
527
|
+
savedSearch: savedSearch
|
|
439
528
|
// Inline (desktop panel) has no home indicator to clear.
|
|
440
529
|
,
|
|
441
530
|
applySafeAreaInset: !inline && modalMode === "bottomSheet"
|
|
442
531
|
}), /*#__PURE__*/_jsx(NetworkFilterScreen, {
|
|
443
|
-
visible: showFilterView && activeTab === "filters" && !
|
|
532
|
+
visible: showFilterView && activeTab === "filters" && !hasSelection,
|
|
444
533
|
paused: paused,
|
|
445
534
|
isPro: isPro,
|
|
446
535
|
filter: filter,
|
|
@@ -450,15 +539,27 @@ function NetworkModalInner({
|
|
|
450
539
|
onAddPattern: addIgnoredPattern,
|
|
451
540
|
onTogglePatternMode: toggleIgnoredPatternMode
|
|
452
541
|
}), /*#__PURE__*/_jsx(NetworkCopyScreen, {
|
|
453
|
-
visible: showFilterView && activeTab === "copy" && !
|
|
542
|
+
visible: showFilterView && activeTab === "copy" && !hasSelection,
|
|
454
543
|
paused: paused,
|
|
455
544
|
isPro: isPro,
|
|
456
545
|
filter: filter,
|
|
457
546
|
ignoredPatterns: ignoredPatterns,
|
|
458
547
|
settings: copySettings,
|
|
459
548
|
onSettingsChange: setCopySettings
|
|
549
|
+
}), /*#__PURE__*/_jsx(NetworkOverridesScreen, {
|
|
550
|
+
visible: overridesAvailable && showOverridesView && !hasSelection && !showFilterView,
|
|
551
|
+
isPro: isPro,
|
|
552
|
+
onUpgradePress: () => showUpgrade("overrides"),
|
|
553
|
+
pendingDraft: pendingOverrideDraft,
|
|
554
|
+
onPendingConsumed: handlePendingOverrideConsumed
|
|
555
|
+
// Inline (desktop panel) has no home indicator to clear.
|
|
556
|
+
,
|
|
557
|
+
applySafeAreaInset: !inline && modalMode === "bottomSheet"
|
|
460
558
|
}), /*#__PURE__*/_jsx(NetworkSavedScreen, {
|
|
461
|
-
visible: showSavedView &&
|
|
559
|
+
visible: showSavedView && !hasSelection && !showFilterView &&
|
|
560
|
+
// Overrides wins in the header branch too — the two must agree, or
|
|
561
|
+
// one screen renders under the other's header.
|
|
562
|
+
!(showOverridesView && overridesAvailable),
|
|
462
563
|
isPro: isPro,
|
|
463
564
|
copySettings: copySettings,
|
|
464
565
|
search: savedSearch,
|
|
@@ -466,7 +567,7 @@ function NetworkModalInner({
|
|
|
466
567
|
onEventPress: handleEventPress,
|
|
467
568
|
onUpgradePress: () => showUpgrade("saved")
|
|
468
569
|
}), /*#__PURE__*/_jsx(NetworkListScreen, {
|
|
469
|
-
visible:
|
|
570
|
+
visible: !hasSelection && !showFilterView && !showSavedView && !(showOverridesView && overridesAvailable),
|
|
470
571
|
paused: paused,
|
|
471
572
|
filter: filter,
|
|
472
573
|
ignoredPatterns: ignoredPatterns,
|
|
@@ -475,12 +576,51 @@ function NetworkModalInner({
|
|
|
475
576
|
onEventPress: handleEventPress,
|
|
476
577
|
onUpgradePress: () => showUpgrade("export")
|
|
477
578
|
})]
|
|
478
|
-
}), /*#__PURE__*/
|
|
579
|
+
}), showHeaderMenu ? /*#__PURE__*/_jsxs(NetworkHeaderMenu, {
|
|
580
|
+
onClose: () => setShowHeaderMenu(false),
|
|
581
|
+
children: [/*#__PURE__*/_jsx(NetworkHeaderMenuItem, {
|
|
582
|
+
icon: /*#__PURE__*/_jsx(Filter, {
|
|
583
|
+
size: 14,
|
|
584
|
+
color: hasActiveFilter ? macOSColors.semantic.info : macOSColors.text.secondary
|
|
585
|
+
}),
|
|
586
|
+
label: "Filters",
|
|
587
|
+
detail: hasActiveFilter ? "Active" : undefined,
|
|
588
|
+
onPress: () => {
|
|
589
|
+
setShowHeaderMenu(false);
|
|
590
|
+
setActiveTab("filters");
|
|
591
|
+
setShowFilterView(true);
|
|
592
|
+
}
|
|
593
|
+
}), /*#__PURE__*/_jsx(NetworkHeaderMenuItem, {
|
|
594
|
+
icon: /*#__PURE__*/_jsx(Bookmark, {
|
|
595
|
+
size: 14,
|
|
596
|
+
color: macOSColors.text.secondary
|
|
597
|
+
}),
|
|
598
|
+
label: "Saved requests",
|
|
599
|
+
onPress: () => {
|
|
600
|
+
setShowHeaderMenu(false);
|
|
601
|
+
setShowSavedView(true);
|
|
602
|
+
}
|
|
603
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
604
|
+
style: styles.menuCopyRow,
|
|
605
|
+
children: /*#__PURE__*/_jsx(NetworkHeaderCopyButton, {
|
|
606
|
+
isPro: isPro,
|
|
607
|
+
filter: filter,
|
|
608
|
+
ignoredPatterns: ignoredPatterns,
|
|
609
|
+
copySettings: copySettings,
|
|
610
|
+
paused: paused,
|
|
611
|
+
onUpgradePress: () => {
|
|
612
|
+
setShowHeaderMenu(false);
|
|
613
|
+
showUpgrade("export");
|
|
614
|
+
},
|
|
615
|
+
label: "Copy requests"
|
|
616
|
+
})
|
|
617
|
+
})]
|
|
618
|
+
}) : null, /*#__PURE__*/_jsx(UpgradeModal, {
|
|
479
619
|
visible: showUpgradeModal,
|
|
480
620
|
onClose: () => setShowUpgradeModal(false),
|
|
481
|
-
featureName: upgradeReason === "saved" ? "Saved Requests" : "Network Export",
|
|
482
|
-
description: upgradeReason === "saved" ? "Keep the requests that matter. Saved requests survive Clear, app restarts, and the 500-request history cap." : "Export network requests to share with your team, debug APIs, or feed into AI tools.",
|
|
483
|
-
benefits: upgradeReason === "saved" ? [`Save up to ${MAX_SAVED} requests (free keeps ${FREE_TIER_SAVED_LIMIT})`, "Failed requests stay put through a Clear", "Snapshots survive an app restart", "Export saved requests to teammates or AI assistants"] : ["Export to JSON, Markdown, or plain text", "Customizable copy presets (URLs only, LLM-ready, full data)", "Share request details with teammates", "Feed network data directly to AI assistants"]
|
|
621
|
+
featureName: upgradeReason === "saved" ? "Saved Requests" : upgradeReason === "overrides" ? "Response Overrides" : "Network Export",
|
|
622
|
+
description: upgradeReason === "saved" ? "Keep the requests that matter. Saved requests survive Clear, app restarts, and the 500-request history cap." : upgradeReason === "overrides" ? "Force any endpoint to fail, stall or return something else — no backend changes, no code changes. Free keeps one active rule." : "Export network requests to share with your team, debug APIs, or feed into AI tools.",
|
|
623
|
+
benefits: upgradeReason === "overrides" ? [`Run ${MAX_OVERRIDE_RULES} rules at once (free keeps ${FREE_TIER_OVERRIDE_LIMIT})`, "Force a 500, a 401 or an empty list in one tap", "Simulate offline, timeouts and slow connections", "Rules survive an app reload, so you can test the boot path"] : upgradeReason === "saved" ? [`Save up to ${MAX_SAVED} requests (free keeps ${FREE_TIER_SAVED_LIMIT})`, "Failed requests stay put through a Clear", "Snapshots survive an app restart", "Export saved requests to teammates or AI assistants"] : ["Export to JSON, Markdown, or plain text", "Customizable copy presets (URLs only, LLM-ready, full data)", "Share request details with teammates", "Feed network data directly to AI assistants"]
|
|
484
624
|
})]
|
|
485
625
|
});
|
|
486
626
|
|
|
@@ -524,6 +664,10 @@ const styles = StyleSheet.create({
|
|
|
524
664
|
flex: 1,
|
|
525
665
|
minHeight: 0
|
|
526
666
|
},
|
|
667
|
+
/** Holds the footer down when the selected ID resolves to no event. */
|
|
668
|
+
detailBodyFallback: {
|
|
669
|
+
flex: 1
|
|
670
|
+
},
|
|
527
671
|
/** Inline shell — fills the host panel; `minHeight: 0` so the list's own
|
|
528
672
|
* FlatList can scroll instead of stretching the column. */
|
|
529
673
|
inlineRoot: {
|
|
@@ -559,6 +703,9 @@ const styles = StyleSheet.create({
|
|
|
559
703
|
marginLeft: 6,
|
|
560
704
|
padding: 4
|
|
561
705
|
},
|
|
706
|
+
menuCopyRow: {
|
|
707
|
+
paddingHorizontal: 6
|
|
708
|
+
},
|
|
562
709
|
headerActionButton: {
|
|
563
710
|
width: 32,
|
|
564
711
|
height: 32,
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
|
|
5
|
+
import { absoluteFill, macOSColors } from "@buoy-gg/shared-ui";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Entering one number, without the keyboard eating the screen.
|
|
9
|
+
*
|
|
10
|
+
* The custom status / delay / times fields used to be inline inputs sitting
|
|
11
|
+
* mid-form. Focusing one raises the keyboard, which on a phone covers roughly
|
|
12
|
+
* the bottom half of the tool — including, depending on where you'd scrolled,
|
|
13
|
+
* the field you were typing into and the Save button you needed next.
|
|
14
|
+
*
|
|
15
|
+
* Pinning the panel to the TOP is the fix, and it's the same one
|
|
16
|
+
* `StorageValueEditModal` reached for: a centred panel gets shoved up or
|
|
17
|
+
* covered the moment the field focuses, and a bottom sheet is simply
|
|
18
|
+
* underneath it. At the top it stays put and stays readable.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately not a reuse of that component — this asks for a number and
|
|
21
|
+
* nothing else. It has no type inference, no raw-JSON escape hatch and no
|
|
22
|
+
* shape-preservation rules, so borrowing 400 lines to avoid writing 100 would
|
|
23
|
+
* be the wrong trade.
|
|
24
|
+
*/
|
|
25
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
26
|
+
export function NetworkNumberPrompt({
|
|
27
|
+
title,
|
|
28
|
+
hint,
|
|
29
|
+
value,
|
|
30
|
+
placeholder = "Number",
|
|
31
|
+
min,
|
|
32
|
+
max,
|
|
33
|
+
onSubmit,
|
|
34
|
+
onCancel
|
|
35
|
+
}) {
|
|
36
|
+
const [text, setText] = useState(value != null ? String(value) : "");
|
|
37
|
+
const submit = () => {
|
|
38
|
+
const trimmed = text.trim();
|
|
39
|
+
if (!trimmed) {
|
|
40
|
+
onSubmit(null);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const parsed = Number(trimmed);
|
|
44
|
+
if (!Number.isFinite(parsed)) {
|
|
45
|
+
onSubmit(null);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Clamped here rather than accepted-then-silently-changed: a field that
|
|
49
|
+
// takes 999 and stores 599 is a field that lies.
|
|
50
|
+
let next = parsed;
|
|
51
|
+
if (min != null) next = Math.max(min, next);
|
|
52
|
+
if (max != null) next = Math.min(max, next);
|
|
53
|
+
onSubmit(next);
|
|
54
|
+
};
|
|
55
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
56
|
+
style: styles.overlay,
|
|
57
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
58
|
+
style: styles.backdrop,
|
|
59
|
+
activeOpacity: 1,
|
|
60
|
+
onPress: onCancel,
|
|
61
|
+
"sentry-label": "ignore dismiss number prompt"
|
|
62
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
63
|
+
style: styles.panel,
|
|
64
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
65
|
+
style: styles.title,
|
|
66
|
+
children: title
|
|
67
|
+
}), /*#__PURE__*/_jsx(TextInput, {
|
|
68
|
+
style: styles.input,
|
|
69
|
+
value: text,
|
|
70
|
+
onChangeText: setText,
|
|
71
|
+
placeholder: placeholder,
|
|
72
|
+
placeholderTextColor: macOSColors.text.muted,
|
|
73
|
+
keyboardType: "number-pad",
|
|
74
|
+
autoFocus: true,
|
|
75
|
+
selectTextOnFocus: true,
|
|
76
|
+
onSubmitEditing: submit,
|
|
77
|
+
returnKeyType: "done",
|
|
78
|
+
"sentry-label": "ignore number prompt input",
|
|
79
|
+
accessibilityLabel: title
|
|
80
|
+
}), hint ? /*#__PURE__*/_jsx(Text, {
|
|
81
|
+
style: styles.hint,
|
|
82
|
+
children: hint
|
|
83
|
+
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
84
|
+
style: styles.actions,
|
|
85
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
86
|
+
onPress: onCancel,
|
|
87
|
+
"sentry-label": "ignore cancel number prompt",
|
|
88
|
+
style: [styles.button, styles.cancelButton],
|
|
89
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
90
|
+
style: styles.cancelText,
|
|
91
|
+
children: "Cancel"
|
|
92
|
+
})
|
|
93
|
+
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
94
|
+
onPress: submit,
|
|
95
|
+
"sentry-label": "ignore save number prompt",
|
|
96
|
+
style: [styles.button, styles.saveButton],
|
|
97
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
98
|
+
style: styles.saveText,
|
|
99
|
+
children: "Set"
|
|
100
|
+
})
|
|
101
|
+
})]
|
|
102
|
+
})]
|
|
103
|
+
})]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const styles = StyleSheet.create({
|
|
107
|
+
overlay: {
|
|
108
|
+
...absoluteFill,
|
|
109
|
+
zIndex: 40
|
|
110
|
+
},
|
|
111
|
+
backdrop: {
|
|
112
|
+
...absoluteFill,
|
|
113
|
+
backgroundColor: "rgba(0,0,0,0.6)"
|
|
114
|
+
},
|
|
115
|
+
panel: {
|
|
116
|
+
marginHorizontal: 12,
|
|
117
|
+
marginTop: 12,
|
|
118
|
+
padding: 14,
|
|
119
|
+
borderRadius: 10,
|
|
120
|
+
borderWidth: 1,
|
|
121
|
+
borderColor: macOSColors.border.hover,
|
|
122
|
+
backgroundColor: macOSColors.background.card,
|
|
123
|
+
gap: 10
|
|
124
|
+
},
|
|
125
|
+
title: {
|
|
126
|
+
fontSize: 13,
|
|
127
|
+
fontWeight: "700",
|
|
128
|
+
color: macOSColors.text.primary
|
|
129
|
+
},
|
|
130
|
+
input: {
|
|
131
|
+
backgroundColor: macOSColors.background.input,
|
|
132
|
+
borderWidth: 1,
|
|
133
|
+
borderColor: macOSColors.border.input,
|
|
134
|
+
borderRadius: 6,
|
|
135
|
+
paddingHorizontal: 12,
|
|
136
|
+
paddingVertical: 10,
|
|
137
|
+
fontSize: 16,
|
|
138
|
+
fontWeight: "700",
|
|
139
|
+
color: macOSColors.text.primary
|
|
140
|
+
},
|
|
141
|
+
hint: {
|
|
142
|
+
fontSize: 10,
|
|
143
|
+
color: macOSColors.text.muted
|
|
144
|
+
},
|
|
145
|
+
actions: {
|
|
146
|
+
flexDirection: "row",
|
|
147
|
+
gap: 8
|
|
148
|
+
},
|
|
149
|
+
button: {
|
|
150
|
+
flex: 1,
|
|
151
|
+
paddingVertical: 10,
|
|
152
|
+
borderRadius: 6,
|
|
153
|
+
alignItems: "center",
|
|
154
|
+
borderWidth: 1
|
|
155
|
+
},
|
|
156
|
+
cancelButton: {
|
|
157
|
+
borderColor: macOSColors.border.default,
|
|
158
|
+
backgroundColor: macOSColors.background.base
|
|
159
|
+
},
|
|
160
|
+
cancelText: {
|
|
161
|
+
fontSize: 12,
|
|
162
|
+
fontWeight: "600",
|
|
163
|
+
color: macOSColors.text.secondary
|
|
164
|
+
},
|
|
165
|
+
saveButton: {
|
|
166
|
+
borderColor: macOSColors.semantic.info,
|
|
167
|
+
backgroundColor: `${macOSColors.semantic.info}22`
|
|
168
|
+
},
|
|
169
|
+
saveText: {
|
|
170
|
+
fontSize: 12,
|
|
171
|
+
fontWeight: "700",
|
|
172
|
+
color: macOSColors.semantic.info
|
|
173
|
+
}
|
|
174
|
+
});
|