@buoy-gg/network 7.0.15 → 7.0.17

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 (107) hide show
  1. package/lib/commonjs/index.web.js +395 -0
  2. package/lib/commonjs/network/components/DataViewer.web.js +879 -0
  3. package/lib/commonjs/network/components/NetworkCopyScreen.web.js +48 -0
  4. package/lib/commonjs/network/components/NetworkCopySettingsView.web.js +1242 -0
  5. package/lib/commonjs/network/components/NetworkDetailScreen.web.js +101 -0
  6. package/lib/commonjs/network/components/NetworkDetailStepper.web.js +296 -0
  7. package/lib/commonjs/network/components/NetworkEventDetailView.web.js +1498 -0
  8. package/lib/commonjs/network/components/NetworkEventItemCompact.web.js +802 -0
  9. package/lib/commonjs/network/components/NetworkFilterScreen.web.js +55 -0
  10. package/lib/commonjs/network/components/NetworkFilterViewV3.web.js +1310 -0
  11. package/lib/commonjs/network/components/NetworkHeaderLive.web.js +604 -0
  12. package/lib/commonjs/network/components/NetworkListScreen.js +2 -1
  13. package/lib/commonjs/network/components/NetworkListScreen.web.js +607 -0
  14. package/lib/commonjs/network/components/NetworkModal.js +12 -3
  15. package/lib/commonjs/network/components/NetworkNumberPrompt.web.js +232 -0
  16. package/lib/commonjs/network/components/NetworkOverrideEditor.web.js +942 -0
  17. package/lib/commonjs/network/components/NetworkOverrideHeaderButton.web.js +133 -0
  18. package/lib/commonjs/network/components/NetworkOverridesButton.web.js +143 -0
  19. package/lib/commonjs/network/components/NetworkOverridesScreen.js +160 -126
  20. package/lib/commonjs/network/components/NetworkOverridesScreen.web.js +830 -0
  21. package/lib/commonjs/network/components/NetworkSavedScreen.web.js +459 -0
  22. package/lib/commonjs/network/components/response-editor/Explorer.web.js +1136 -0
  23. package/lib/commonjs/network/hooks/useNetworkEventList.js +12 -2
  24. package/lib/commonjs/network/overrides/applyToXhr.js +276 -15
  25. package/lib/commonjs/network/utils/generateNetworkCopyText.js +32 -5
  26. package/lib/commonjs/network/utils/networkEventStore.js +12 -4
  27. package/lib/commonjs/network/utils/networkListener.js +1 -16
  28. package/lib/module/index.web.js +81 -0
  29. package/lib/module/network/components/DataViewer.web.js +875 -0
  30. package/lib/module/network/components/NetworkCopyScreen.web.js +44 -0
  31. package/lib/module/network/components/NetworkCopySettingsView.web.js +1237 -0
  32. package/lib/module/network/components/NetworkDetailScreen.web.js +97 -0
  33. package/lib/module/network/components/NetworkDetailStepper.web.js +294 -0
  34. package/lib/module/network/components/NetworkEventDetailView.web.js +1496 -0
  35. package/lib/module/network/components/NetworkEventItemCompact.web.js +800 -0
  36. package/lib/module/network/components/NetworkFilterScreen.web.js +51 -0
  37. package/lib/module/network/components/NetworkFilterViewV3.web.js +1306 -0
  38. package/lib/module/network/components/NetworkHeaderLive.web.js +598 -0
  39. package/lib/module/network/components/NetworkListScreen.js +2 -1
  40. package/lib/module/network/components/NetworkListScreen.web.js +605 -0
  41. package/lib/module/network/components/NetworkModal.js +12 -3
  42. package/lib/module/network/components/NetworkNumberPrompt.web.js +230 -0
  43. package/lib/module/network/components/NetworkOverrideEditor.web.js +939 -0
  44. package/lib/module/network/components/NetworkOverrideHeaderButton.web.js +131 -0
  45. package/lib/module/network/components/NetworkOverridesButton.web.js +141 -0
  46. package/lib/module/network/components/NetworkOverridesScreen.js +161 -127
  47. package/lib/module/network/components/NetworkOverridesScreen.web.js +826 -0
  48. package/lib/module/network/components/NetworkSavedScreen.web.js +457 -0
  49. package/lib/module/network/components/response-editor/Explorer.web.js +1133 -0
  50. package/lib/module/network/hooks/useNetworkEventList.js +12 -2
  51. package/lib/module/network/overrides/applyToXhr.js +275 -15
  52. package/lib/module/network/utils/generateNetworkCopyText.js +28 -1
  53. package/lib/module/network/utils/networkEventStore.js +12 -4
  54. package/lib/module/network/utils/networkListener.js +1 -16
  55. package/lib/typescript/index.web.d.ts +56 -0
  56. package/lib/typescript/index.web.d.ts.map +1 -0
  57. package/lib/typescript/network/components/DataViewer.web.d.ts +78 -0
  58. package/lib/typescript/network/components/DataViewer.web.d.ts.map +1 -0
  59. package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts +27 -0
  60. package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts.map +1 -0
  61. package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts +68 -0
  62. package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts.map +1 -0
  63. package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts +45 -0
  64. package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts.map +1 -0
  65. package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts +40 -0
  66. package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts.map +1 -0
  67. package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts +67 -0
  68. package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts.map +1 -0
  69. package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts +58 -0
  70. package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts.map +1 -0
  71. package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts +28 -0
  72. package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts.map +1 -0
  73. package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts +74 -0
  74. package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts.map +1 -0
  75. package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts +112 -0
  76. package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts.map +1 -0
  77. package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
  78. package/lib/typescript/network/components/NetworkListScreen.web.d.ts +63 -0
  79. package/lib/typescript/network/components/NetworkListScreen.web.d.ts.map +1 -0
  80. package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
  81. package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts +38 -0
  82. package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts.map +1 -0
  83. package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts +43 -0
  84. package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts.map +1 -0
  85. package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts +32 -0
  86. package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts.map +1 -0
  87. package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts +26 -0
  88. package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts.map +1 -0
  89. package/lib/typescript/network/components/NetworkOverridesScreen.d.ts +9 -0
  90. package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -1
  91. package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts +69 -0
  92. package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts.map +1 -0
  93. package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts +67 -0
  94. package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts.map +1 -0
  95. package/lib/typescript/network/components/response-editor/Explorer.web.d.ts +56 -0
  96. package/lib/typescript/network/components/response-editor/Explorer.web.d.ts.map +1 -0
  97. package/lib/typescript/network/hooks/useNetworkEventList.d.ts +10 -0
  98. package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +1 -1
  99. package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts.map +1 -1
  100. package/lib/typescript/network/overrides/applyToXhr.d.ts +40 -14
  101. package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -1
  102. package/lib/typescript/network/utils/generateNetworkCopyText.d.ts +34 -1
  103. package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +1 -1
  104. package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
  105. package/lib/typescript/network/utils/networkListener.d.ts +0 -1
  106. package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
  107. package/package.json +2 -2
@@ -0,0 +1,1237 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NetworkCopySettingsView.web — DOM twin of NetworkCopySettingsView.tsx.
5
+ *
6
+ * The COPY tab's configurator: quick presets (URLs / LLM / JSON / Full /
7
+ * Custom), the Include toggles, the output-format pick, and the collapsible
8
+ * live preview with its size warnings and copy action. Same preset configs,
9
+ * preset detection, option-change routing (`group::key` ids), mock-request
10
+ * fallback, size estimation and preview/copy text generation as the native
11
+ * file — all copied verbatim.
12
+ *
13
+ * Natively the chrome is shared-ui's `DynamicFilterView`; the web shim
14
+ * doesn't ship it, so this twin renders the exact subtree DynamicFilterView
15
+ * produces FOR THIS CONFIG (no tabs, no filter-summary, no pattern sections —
16
+ * those config branches are unused here), same precedent as
17
+ * NetworkFilterViewV3.web:
18
+ * 1. Facet-card sections — Presets (icon + label), Include (label-only
19
+ * toggles), Format (icon + label); single active-card treatment
20
+ * (primary fill + 2px ring + glow).
21
+ * 2. PREVIEW section — SectionHeader (Eye icon, title, header actions =
22
+ * status label + toolbar copy button) over the recessed content box;
23
+ * collapsed by default behind the Show Preview summary (request count +
24
+ * estimated size + large-payload warnings), exactly like native.
25
+ *
26
+ * shared-ui RN chrome replicated as DOM with provenance:
27
+ * - `buoyColors` / `macOSColors` — inlined verbatim subsets from
28
+ * shared/src/ui/gameUI/constants/ (DynamicFilterView + SectionHeader use
29
+ * buoyColors; the preview widgets use macOSColors directly).
30
+ * - Icons — inline lucide SVGs (shared-ui's icons are lucide re-exports).
31
+ * - `ToolbarCopyButton` — the DataViewer.web `CopyButton` twin at size 14
32
+ * (the native preset is exactly `CopyButton size={14}` with default
33
+ * colors); copies via navigator.clipboard with the same 1500ms feedback.
34
+ * - `DataViewer` (JSON-format preview) — the DataViewer.web twin.
35
+ * - `formatRelativeTime` — copied verbatim from
36
+ * shared/src/utils/time/formatRelativeTime.ts.
37
+ * - Native card glow (`shadowColor` primary, opacity 0.3, radius 8) → an
38
+ * equivalent CSS box-shadow.
39
+ *
40
+ * Intentional deviations (all logged):
41
+ * - License gating isn't part of the web port: native reads
42
+ * `useFeatureGate()` and wraps the view in a `ProFeatureBanner` for free
43
+ * users; here `isPro` is a constant true, so that branch never renders and
44
+ * is not ported. Copy is likewise ungated (native's CopyButton shows a
45
+ * Pro modal via @buoy-gg/license).
46
+ * - `useTickEveryMinute` (shared TickProvider context, 5s cadence as mounted
47
+ * by NetworkModal) → the same module-level shared 5s interval hook the
48
+ * compact row twin uses.
49
+ * - Native's `previewData` memo is dead code (computed, never read) and is
50
+ * dropped here — the host tsconfig runs `noUnusedLocals`.
51
+ * - `selectable={isPro}` on the preview text → `userSelect: "text"` (isPro
52
+ * is constant-true on web).
53
+ * - `CopySettings` / `DEFAULT_COPY_SETTINGS` are re-declared against the
54
+ * platform-free type in `generateNetworkCopyText` (the native module would
55
+ * drag react-native in); the shape is byte-identical.
56
+ *
57
+ * @platform React DOM Web
58
+ */
59
+
60
+ import { useCallback, useEffect, useMemo, useState } from "react";
61
+ import { CopyButton, DataViewer } from "./DataViewer.web";
62
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
63
+ /** NetworkCopySettingsView.tsx `DEFAULT_COPY_SETTINGS`, verbatim. */
64
+ export const DEFAULT_COPY_SETTINGS = {
65
+ includeMethod: true,
66
+ includeStatus: true,
67
+ includeDuration: true,
68
+ includeTimestamp: true,
69
+ includeClient: true,
70
+ includeSizes: true,
71
+ includeErrors: true,
72
+ includeRequestHeaders: true,
73
+ includeResponseHeaders: true,
74
+ includeRequestBody: true,
75
+ includeResponseBody: true,
76
+ bodySizeThreshold: 10,
77
+ format: "markdown",
78
+ filterMode: "all"
79
+ };
80
+
81
+ // =============================
82
+ // Native palettes (provenance)
83
+ // =============================
84
+
85
+ /** buoyColors subset — shared/src/ui/gameUI/constants/gameUIColors.ts, verbatim. */
86
+ const buoyColors = {
87
+ base: "#121212",
88
+ card: "#1A1A1A",
89
+ hover: "#242424",
90
+ border: "#333333",
91
+ borderStrong: "#444444",
92
+ text: "#E0E0E0",
93
+ primary: "#20C997"
94
+ };
95
+
96
+ /** macOSColors subset — shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts, verbatim. */
97
+ const macOSColors = {
98
+ background: {
99
+ input: "#26262A",
100
+ hover: "#1D1D1F"
101
+ },
102
+ border: {
103
+ default: "#2D2D2F"
104
+ },
105
+ text: {
106
+ primary: "#F5F5F7",
107
+ secondary: "#A1A1A6",
108
+ muted: "#8E8E93"
109
+ },
110
+ semantic: {
111
+ success: "#34C759",
112
+ error: "#FF453A",
113
+ errorBackground: "rgba(255, 69, 58, 0.15)",
114
+ warning: "#FFEB3B",
115
+ warningBackground: "rgba(255, 235, 59, 0.15)",
116
+ info: "#00B8E6"
117
+ }
118
+ };
119
+
120
+ // =============================
121
+ // Inline lucide icons (shared-ui icons are lucide re-exports)
122
+ // =============================
123
+
124
+ function Svg({
125
+ size = 14,
126
+ color = "currentColor",
127
+ children
128
+ }) {
129
+ return /*#__PURE__*/_jsx("svg", {
130
+ width: size,
131
+ height: size,
132
+ viewBox: "0 0 24 24",
133
+ fill: "none",
134
+ stroke: color,
135
+ strokeWidth: 2,
136
+ strokeLinecap: "round",
137
+ strokeLinejoin: "round",
138
+ style: {
139
+ flexShrink: 0,
140
+ display: "block"
141
+ },
142
+ children: children
143
+ });
144
+ }
145
+ const FileText = p => /*#__PURE__*/_jsxs(Svg, {
146
+ ...p,
147
+ children: [/*#__PURE__*/_jsx("path", {
148
+ d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
149
+ }), /*#__PURE__*/_jsx("path", {
150
+ d: "M14 2v4a2 2 0 0 0 2 2h4"
151
+ }), /*#__PURE__*/_jsx("path", {
152
+ d: "M10 9H8"
153
+ }), /*#__PURE__*/_jsx("path", {
154
+ d: "M16 13H8"
155
+ }), /*#__PURE__*/_jsx("path", {
156
+ d: "M16 17H8"
157
+ })]
158
+ });
159
+ const FileCode = p => /*#__PURE__*/_jsxs(Svg, {
160
+ ...p,
161
+ children: [/*#__PURE__*/_jsx("path", {
162
+ d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"
163
+ }), /*#__PURE__*/_jsx("path", {
164
+ d: "M14 2v4a2 2 0 0 0 2 2h4"
165
+ }), /*#__PURE__*/_jsx("path", {
166
+ d: "M10 12.5 8 15l2 2.5"
167
+ }), /*#__PURE__*/_jsx("path", {
168
+ d: "m14 12.5 2 2.5-2 2.5"
169
+ })]
170
+ });
171
+ const Link = p => /*#__PURE__*/_jsxs(Svg, {
172
+ ...p,
173
+ children: [/*#__PURE__*/_jsx("path", {
174
+ d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"
175
+ }), /*#__PURE__*/_jsx("path", {
176
+ d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
177
+ })]
178
+ });
179
+ const Zap = p => /*#__PURE__*/_jsx(Svg, {
180
+ ...p,
181
+ children: /*#__PURE__*/_jsx("polygon", {
182
+ points: "13 2 3 14 12 14 11 22 21 10 12 10 13 2"
183
+ })
184
+ });
185
+ const Settings = p => /*#__PURE__*/_jsxs(Svg, {
186
+ ...p,
187
+ children: [/*#__PURE__*/_jsx("path", {
188
+ d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"
189
+ }), /*#__PURE__*/_jsx("circle", {
190
+ cx: "12",
191
+ cy: "12",
192
+ r: "3"
193
+ })]
194
+ });
195
+ const Hash = p => /*#__PURE__*/_jsxs(Svg, {
196
+ ...p,
197
+ children: [/*#__PURE__*/_jsx("line", {
198
+ x1: "4",
199
+ x2: "20",
200
+ y1: "9",
201
+ y2: "9"
202
+ }), /*#__PURE__*/_jsx("line", {
203
+ x1: "4",
204
+ x2: "20",
205
+ y1: "15",
206
+ y2: "15"
207
+ }), /*#__PURE__*/_jsx("line", {
208
+ x1: "10",
209
+ x2: "8",
210
+ y1: "3",
211
+ y2: "21"
212
+ }), /*#__PURE__*/_jsx("line", {
213
+ x1: "16",
214
+ x2: "14",
215
+ y1: "3",
216
+ y2: "21"
217
+ })]
218
+ });
219
+ const Eye = p => /*#__PURE__*/_jsxs(Svg, {
220
+ ...p,
221
+ children: [/*#__PURE__*/_jsx("path", {
222
+ d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"
223
+ }), /*#__PURE__*/_jsx("circle", {
224
+ cx: "12",
225
+ cy: "12",
226
+ r: "3"
227
+ })]
228
+ });
229
+ const ChevronDown = p => /*#__PURE__*/_jsx(Svg, {
230
+ ...p,
231
+ children: /*#__PURE__*/_jsx("path", {
232
+ d: "m6 9 6 6 6-6"
233
+ })
234
+ });
235
+ const ChevronUp = p => /*#__PURE__*/_jsx(Svg, {
236
+ ...p,
237
+ children: /*#__PURE__*/_jsx("path", {
238
+ d: "m18 15-6-6-6 6"
239
+ })
240
+ });
241
+ const AlertTriangle = p => /*#__PURE__*/_jsxs(Svg, {
242
+ ...p,
243
+ children: [/*#__PURE__*/_jsx("path", {
244
+ d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"
245
+ }), /*#__PURE__*/_jsx("path", {
246
+ d: "M12 9v4"
247
+ }), /*#__PURE__*/_jsx("path", {
248
+ d: "M12 17h.01"
249
+ })]
250
+ });
251
+
252
+ // =============================
253
+ // Shared tick (DEVIATION: replaces shared-ui TickProvider/useTickEveryMinute)
254
+ // =============================
255
+
256
+ /**
257
+ * One module-level 5s clock — the cadence NetworkModal's `TickProvider` gives
258
+ * the native view (default `intervalMs` 5_000). Runs only while a consumer is
259
+ * mounted. Same pattern as NetworkEventItemCompact.web.
260
+ */
261
+ const tickListeners = new Set();
262
+ let tickInterval = null;
263
+ function subscribeTick(listener) {
264
+ tickListeners.add(listener);
265
+ if (tickInterval == null) {
266
+ tickInterval = setInterval(() => {
267
+ const now = Date.now();
268
+ tickListeners.forEach(l => l(now));
269
+ }, 5_000);
270
+ }
271
+ return () => {
272
+ tickListeners.delete(listener);
273
+ if (tickListeners.size === 0 && tickInterval != null) {
274
+ clearInterval(tickInterval);
275
+ tickInterval = null;
276
+ }
277
+ };
278
+ }
279
+ function useSharedTick() {
280
+ const [tick, setTick] = useState(() => Date.now());
281
+ useEffect(() => subscribeTick(setTick), []);
282
+ return tick;
283
+ }
284
+
285
+ /** shared/src/utils/time/formatRelativeTime.ts — verbatim. */
286
+ function formatRelativeTime(timestamp, currentTime = Date.now()) {
287
+ const timestampMs = timestamp instanceof Date ? timestamp.getTime() : timestamp;
288
+ const seconds = Math.floor((currentTime - timestampMs) / 1000);
289
+ if (seconds < 0) {
290
+ return "just now";
291
+ }
292
+ if (seconds < 60) {
293
+ return seconds === 0 ? "just now" : `${seconds}s ago`;
294
+ }
295
+ const minutes = Math.floor(seconds / 60);
296
+ if (minutes < 60) {
297
+ return `${minutes}m ago`;
298
+ }
299
+ const hours = Math.floor(minutes / 60);
300
+ if (hours < 24) {
301
+ return `${hours}h ago`;
302
+ }
303
+ const days = Math.floor(hours / 24);
304
+ return `${days}d ago`;
305
+ }
306
+
307
+ // =============================
308
+ // Config + pure helpers (copied verbatim from the native file)
309
+ // =============================
310
+
311
+ // Preset configurations for comparison
312
+ const PRESET_CONFIGS = {
313
+ urls: {
314
+ includeMethod: true,
315
+ includeStatus: false,
316
+ includeDuration: false,
317
+ includeTimestamp: false,
318
+ includeClient: false,
319
+ includeSizes: false,
320
+ includeErrors: false,
321
+ includeRequestHeaders: false,
322
+ includeResponseHeaders: false,
323
+ includeRequestBody: false,
324
+ includeResponseBody: false,
325
+ bodySizeThreshold: 10,
326
+ format: "plaintext",
327
+ filterMode: "all"
328
+ },
329
+ llm: {
330
+ includeMethod: true,
331
+ includeStatus: true,
332
+ includeDuration: true,
333
+ includeTimestamp: true,
334
+ includeClient: true,
335
+ includeSizes: true,
336
+ includeErrors: true,
337
+ includeRequestHeaders: true,
338
+ includeResponseHeaders: true,
339
+ includeRequestBody: true,
340
+ includeResponseBody: true,
341
+ bodySizeThreshold: 10,
342
+ format: "markdown",
343
+ filterMode: "all"
344
+ },
345
+ json: {
346
+ includeMethod: true,
347
+ includeStatus: true,
348
+ includeDuration: true,
349
+ includeTimestamp: true,
350
+ includeClient: true,
351
+ includeSizes: true,
352
+ includeErrors: true,
353
+ includeRequestHeaders: true,
354
+ includeResponseHeaders: true,
355
+ includeRequestBody: true,
356
+ includeResponseBody: true,
357
+ bodySizeThreshold: -1,
358
+ format: "json",
359
+ filterMode: "all"
360
+ },
361
+ full: {
362
+ includeMethod: true,
363
+ includeStatus: true,
364
+ includeDuration: true,
365
+ includeTimestamp: true,
366
+ includeClient: true,
367
+ includeSizes: true,
368
+ includeErrors: true,
369
+ includeRequestHeaders: true,
370
+ includeResponseHeaders: true,
371
+ includeRequestBody: true,
372
+ includeResponseBody: true,
373
+ bodySizeThreshold: -1,
374
+ format: "markdown",
375
+ filterMode: "all"
376
+ }
377
+ };
378
+ // Detect which preset matches current settings
379
+ function detectActivePreset(settings) {
380
+ for (const [presetName, presetConfig] of Object.entries(PRESET_CONFIGS)) {
381
+ const matches = Object.keys(presetConfig).every(key => presetConfig[key] === settings[key]);
382
+ if (matches) return presetName;
383
+ }
384
+ return null; // No preset matches = Custom
385
+ }
386
+
387
+ // Size thresholds for warnings
388
+ const SIZE_WARNING_THRESHOLD = 100 * 1024; // 100KB - show warning
389
+ const SIZE_DANGER_THRESHOLD = 500 * 1024; // 500KB - show danger warning
390
+
391
+ // Helper to format bytes for display
392
+ function formatBytes(bytes) {
393
+ if (bytes < 1024) return `${bytes} B`;
394
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
395
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
396
+ }
397
+
398
+ // =============================
399
+ // DynamicFilterView chrome twins for this config (provenance:
400
+ // shared/src/ui/components/DynamicFilterView.tsx + SectionHeader.tsx)
401
+ // =============================
402
+
403
+ /** SectionHeader DOM twin — container row, 16/8 padding, minHeight 40. */
404
+ function SectionHeaderRow({
405
+ icon: Icon,
406
+ iconColor,
407
+ title,
408
+ actions
409
+ }) {
410
+ return /*#__PURE__*/_jsxs("div", {
411
+ style: styles.sectionHeader,
412
+ children: [Icon ? /*#__PURE__*/_jsx("span", {
413
+ style: styles.sectionHeaderIcon,
414
+ children: /*#__PURE__*/_jsx(Icon, {
415
+ size: 12,
416
+ color: iconColor ?? buoyColors.primary
417
+ })
418
+ }) : null, /*#__PURE__*/_jsx("span", {
419
+ style: styles.sectionHeaderTitle,
420
+ children: title
421
+ }), actions ? /*#__PURE__*/_jsx("span", {
422
+ style: styles.sectionHeaderActions,
423
+ children: actions
424
+ }) : null]
425
+ });
426
+ }
427
+
428
+ /** DynamicFilterView's facet-card grid section for a `custom` section. */
429
+ function FilterCardSection({
430
+ title,
431
+ options,
432
+ onOptionPress
433
+ }) {
434
+ return /*#__PURE__*/_jsxs("div", {
435
+ style: styles.section,
436
+ children: [/*#__PURE__*/_jsx(SectionHeaderRow, {
437
+ title: title
438
+ }), /*#__PURE__*/_jsx("div", {
439
+ style: styles.filterGrid,
440
+ children: options.map(option => {
441
+ const Icon = option.icon;
442
+ return /*#__PURE__*/_jsxs("button", {
443
+ type: "button",
444
+ onClick: () => onOptionPress(option.id, option.value),
445
+ style: {
446
+ ...styles.filterCard,
447
+ ...(option.isActive ? styles.activeFilterCard : null)
448
+ },
449
+ children: [Icon ? /*#__PURE__*/_jsx(Icon, {
450
+ size: 24,
451
+ color: option.color
452
+ }) : null, /*#__PURE__*/_jsx("span", {
453
+ style: styles.filterLabelCompact,
454
+ children: option.label
455
+ })]
456
+ }, option.id);
457
+ })
458
+ })]
459
+ });
460
+ }
461
+
462
+ // =============================
463
+ // View (structure + logic mirror the native file 1:1)
464
+ // =============================
465
+
466
+ export function NetworkCopySettingsView({
467
+ settings,
468
+ onSettingsChange,
469
+ events = []
470
+ }) {
471
+ // DEVIATION: native reads `useFeatureGate()`; license gating isn't part of
472
+ // the web port, so the free-user ProFeatureBanner branch never renders.
473
+ const isPro = true;
474
+
475
+ // Use tick for updating relative time
476
+ const tick = useSharedTick();
477
+
478
+ // State for preview expansion - collapsed by default to prevent crashes
479
+ const [isPreviewExpanded, setIsPreviewExpanded] = useState(false);
480
+
481
+ // Auto-detect if we have live data
482
+ const hasLiveData = events.length > 0;
483
+
484
+ // Get the most recent event timestamp for "last updated" display
485
+ const lastEventTimestamp = useMemo(() => {
486
+ if (!hasLiveData) return null;
487
+ const timestamps = events.map(e => typeof e.timestamp === "number" ? e.timestamp : new Date(e.timestamp).getTime());
488
+ return Math.max(...timestamps);
489
+ }, [events, hasLiveData]);
490
+
491
+ // Detect active preset
492
+ const activePreset = useMemo(() => detectActivePreset(settings), [settings]);
493
+
494
+ // Quick preset handlers
495
+ const applyUrlsPreset = useCallback(() => {
496
+ onSettingsChange(PRESET_CONFIGS.urls);
497
+ }, [onSettingsChange]);
498
+ const applyLLMPreset = useCallback(() => {
499
+ onSettingsChange(PRESET_CONFIGS.llm);
500
+ }, [onSettingsChange]);
501
+ const applyJSONPreset = useCallback(() => {
502
+ onSettingsChange(PRESET_CONFIGS.json);
503
+ }, [onSettingsChange]);
504
+ const applyFullPreset = useCallback(() => {
505
+ onSettingsChange(PRESET_CONFIGS.full);
506
+ }, [onSettingsChange]);
507
+
508
+ // Handle individual option changes
509
+ const handleOptionChange = useCallback((optionId, value) => {
510
+ const [group, key] = optionId.split("::");
511
+ if (group === "preset") {
512
+ switch (key) {
513
+ case "urls":
514
+ applyUrlsPreset();
515
+ break;
516
+ case "llm":
517
+ applyLLMPreset();
518
+ break;
519
+ case "json":
520
+ applyJSONPreset();
521
+ break;
522
+ case "full":
523
+ applyFullPreset();
524
+ break;
525
+ }
526
+ return;
527
+ }
528
+ if (group === "requestInfo") {
529
+ // Toggle boolean value
530
+ onSettingsChange({
531
+ ...settings,
532
+ [key]: !settings[key]
533
+ });
534
+ return;
535
+ }
536
+ if (group === "headers") {
537
+ // Toggle boolean value
538
+ onSettingsChange({
539
+ ...settings,
540
+ [key]: !settings[key]
541
+ });
542
+ return;
543
+ }
544
+ if (group === "body") {
545
+ if (key === "bodySizeThreshold") {
546
+ onSettingsChange({
547
+ ...settings,
548
+ bodySizeThreshold: value
549
+ });
550
+ } else {
551
+ // Toggle boolean value
552
+ onSettingsChange({
553
+ ...settings,
554
+ [key]: !settings[key]
555
+ });
556
+ }
557
+ return;
558
+ }
559
+ if (group === "format") {
560
+ onSettingsChange({
561
+ ...settings,
562
+ format: value
563
+ });
564
+ return;
565
+ }
566
+ if (group === "filter") {
567
+ onSettingsChange({
568
+ ...settings,
569
+ filterMode: value
570
+ });
571
+ }
572
+ }, [settings, onSettingsChange, applyUrlsPreset, applyLLMPreset, applyJSONPreset, applyFullPreset]);
573
+
574
+ // Mock data for preview
575
+ const mockRequestData = useMemo(() => ({
576
+ method: "POST",
577
+ url: "https://api.example.com/v1/users",
578
+ status: 201,
579
+ duration: 342,
580
+ timestamp: new Date().toISOString(),
581
+ requestHeaders: {
582
+ "Content-Type": "application/json",
583
+ Authorization: "Bearer ***"
584
+ },
585
+ responseHeaders: {
586
+ "Content-Type": "application/json",
587
+ "X-Request-ID": "abc123"
588
+ },
589
+ requestBody: {
590
+ name: "John Doe",
591
+ email: "john@example.com"
592
+ },
593
+ responseBody: {
594
+ id: "user_123",
595
+ name: "John Doe",
596
+ email: "john@example.com",
597
+ createdAt: "2025-01-10T12:00:00Z"
598
+ }
599
+ }), []);
600
+
601
+ // Estimate the size of the preview content to warn users about large payloads
602
+ const estimatedSize = useMemo(() => {
603
+ const dataSource = hasLiveData ? events : [mockRequestData];
604
+ let totalSize = 0;
605
+ for (const eventData of dataSource) {
606
+ // Estimate based on what's included
607
+ if (settings.includeMethod) {
608
+ totalSize += (eventData.url?.length || 0) + 10;
609
+ }
610
+ if (settings.includeStatus) {
611
+ totalSize += 30;
612
+ }
613
+ if (settings.includeTimestamp) {
614
+ totalSize += 30;
615
+ }
616
+ if (settings.includeRequestHeaders) {
617
+ try {
618
+ totalSize += JSON.stringify(eventData.requestHeaders || {}).length;
619
+ } catch {
620
+ totalSize += 100;
621
+ }
622
+ }
623
+ if (settings.includeRequestBody) {
624
+ const body = "requestBody" in eventData ? eventData.requestBody : eventData.requestData;
625
+ try {
626
+ totalSize += JSON.stringify(body || {}).length;
627
+ } catch {
628
+ totalSize += 100;
629
+ }
630
+ }
631
+ if (settings.includeResponseBody) {
632
+ const body = "responseBody" in eventData ? eventData.responseBody : eventData.responseData;
633
+ try {
634
+ totalSize += JSON.stringify(body || {}).length;
635
+ } catch {
636
+ totalSize += 100;
637
+ }
638
+ }
639
+ }
640
+ return totalSize;
641
+ }, [hasLiveData, events, mockRequestData, settings]);
642
+
643
+ // Determine warning level based on estimated size
644
+ const sizeWarningLevel = useMemo(() => {
645
+ if (estimatedSize >= SIZE_DANGER_THRESHOLD) return "danger";
646
+ if (estimatedSize >= SIZE_WARNING_THRESHOLD) return "warning";
647
+ return "none";
648
+ }, [estimatedSize]);
649
+
650
+ // Generate copy text - should match preview content exactly
651
+ const generateCopyText = useCallback(() => {
652
+ const dataSource = hasLiveData ? events : [mockRequestData];
653
+ const {
654
+ format,
655
+ includeMethod,
656
+ includeStatus,
657
+ includeTimestamp,
658
+ includeRequestHeaders,
659
+ includeRequestBody,
660
+ includeResponseBody
661
+ } = settings;
662
+
663
+ // Build filtered data for a single event
664
+ const buildFilteredData = eventData => {
665
+ const data = {};
666
+ if (includeMethod) {
667
+ data.method = eventData.method;
668
+ data.url = eventData.url;
669
+ }
670
+ if (includeStatus) {
671
+ data.status = eventData.status;
672
+ data.duration = eventData.duration;
673
+ }
674
+ if (includeTimestamp) {
675
+ data.timestamp = eventData.timestamp;
676
+ }
677
+ if (includeRequestHeaders) {
678
+ data.requestHeaders = eventData.requestHeaders;
679
+ }
680
+ if (includeRequestBody) {
681
+ data.requestBody = "requestBody" in eventData ? eventData.requestBody : eventData.requestData;
682
+ }
683
+ if (includeResponseBody) {
684
+ data.responseBody = "responseBody" in eventData ? eventData.responseBody : eventData.responseData;
685
+ }
686
+ return data;
687
+ };
688
+ if (format === "json") {
689
+ const allFilteredData = dataSource.map(buildFilteredData);
690
+ return JSON.stringify(allFilteredData, null, 2);
691
+ }
692
+ if (format === "markdown") {
693
+ const allRequests = dataSource.map((eventData, index) => {
694
+ const sections = [];
695
+ sections.push(`# Request ${index + 1}`);
696
+ if (includeMethod) {
697
+ sections.push(`\n**${eventData.method}** ${eventData.url}`);
698
+ }
699
+ const metaInfo = [];
700
+ if (includeStatus) {
701
+ metaInfo.push(`**Status:** ${eventData.status}`);
702
+ metaInfo.push(`**Duration:** ${eventData.duration}ms`);
703
+ }
704
+ if (includeTimestamp) {
705
+ metaInfo.push(`**Timestamp:** ${eventData.timestamp}`);
706
+ }
707
+ if (metaInfo.length > 0) {
708
+ sections.push("\n" + metaInfo.join("\n"));
709
+ }
710
+ if (includeRequestHeaders) {
711
+ const headers = eventData.requestHeaders || {};
712
+ sections.push(`\n## Request Headers\n\`\`\`json\n${JSON.stringify(headers, null, 2)}\n\`\`\``);
713
+ }
714
+ if (includeRequestBody) {
715
+ const body = "requestBody" in eventData ? eventData.requestBody : eventData.requestData;
716
+ sections.push(`\n## Request Body\n\`\`\`json\n${JSON.stringify(body || {}, null, 2)}\n\`\`\``);
717
+ }
718
+ if (includeResponseBody) {
719
+ const body = "responseBody" in eventData ? eventData.responseBody : eventData.responseData;
720
+ sections.push(`\n## Response Body\n\`\`\`json\n${JSON.stringify(body || {}, null, 2)}\n\`\`\``);
721
+ }
722
+ return sections.join("\n");
723
+ });
724
+ return allRequests.join("\n\n---\n\n") || "No data included";
725
+ }
726
+
727
+ // Plaintext
728
+ const allRequests = dataSource.map((eventData, index) => {
729
+ const lines = [];
730
+ lines.push(`Request ${index + 1}:`);
731
+ if (includeMethod) {
732
+ lines.push(`${eventData.method} ${eventData.url}`);
733
+ }
734
+ if (includeStatus) {
735
+ lines.push(`Status: ${eventData.status}`);
736
+ lines.push(`Duration: ${eventData.duration}ms`);
737
+ }
738
+ if (includeTimestamp) {
739
+ lines.push(`Timestamp: ${eventData.timestamp}`);
740
+ }
741
+ if (includeRequestBody) {
742
+ const body = "requestBody" in eventData ? eventData.requestBody : eventData.requestData;
743
+ lines.push("\nRequest:");
744
+ lines.push(JSON.stringify(body || {}, null, 2));
745
+ }
746
+ if (includeResponseBody) {
747
+ const body = "responseBody" in eventData ? eventData.responseBody : eventData.responseData;
748
+ lines.push("\nResponse:");
749
+ lines.push(JSON.stringify(body || {}, null, 2));
750
+ }
751
+ return lines.join("\n");
752
+ });
753
+ return allRequests.join("\n\n---\n\n") || "No data included";
754
+ }, [hasLiveData, events, mockRequestData, settings]);
755
+
756
+ // Generate the expanded preview content (only called when expanded)
757
+ const generateExpandedPreviewContent = useCallback(() => {
758
+ if (settings.format === "json") {
759
+ const dataSource = hasLiveData ? events : [mockRequestData];
760
+ const buildFilteredData = eventData => {
761
+ const data = {};
762
+ if (settings.includeMethod) {
763
+ data.method = eventData.method;
764
+ data.url = eventData.url;
765
+ }
766
+ if (settings.includeStatus) {
767
+ data.status = eventData.status;
768
+ data.duration = eventData.duration;
769
+ }
770
+ if (settings.includeTimestamp) {
771
+ data.timestamp = eventData.timestamp;
772
+ }
773
+ if (settings.includeRequestHeaders) {
774
+ data.requestHeaders = eventData.requestHeaders;
775
+ }
776
+ if (settings.includeRequestBody) {
777
+ data.requestBody = "requestBody" in eventData ? eventData.requestBody : eventData.requestData;
778
+ }
779
+ if (settings.includeResponseBody) {
780
+ data.responseBody = "responseBody" in eventData ? eventData.responseBody : eventData.responseData;
781
+ }
782
+ return data;
783
+ };
784
+ const allFilteredData = dataSource.map(buildFilteredData);
785
+ return /*#__PURE__*/_jsx(DataViewer, {
786
+ data: allFilteredData,
787
+ title: "",
788
+ showTypeFilter: false
789
+ });
790
+ }
791
+
792
+ // Markdown / plaintext share the same monospace scroll box; the STRING is
793
+ // exactly what `generateCopyText` produces for these formats (native
794
+ // duplicates the builders — the outputs are identical, so the twin feeds
795
+ // the preview from the one builder rather than carrying a second copy).
796
+ const text = generateCopyText();
797
+ return /*#__PURE__*/_jsx("div", {
798
+ style: styles.previewScroll,
799
+ children: /*#__PURE__*/_jsx("div", {
800
+ style: {
801
+ ...(settings.format === "markdown" ? styles.markdownText : styles.plaintextText),
802
+ // Native `selectable={isPro}` — constant-true on web (deviations).
803
+ userSelect: isPro ? "text" : "none"
804
+ },
805
+ children: text || "No data included"
806
+ })
807
+ });
808
+ }, [settings, hasLiveData, events, mockRequestData, generateCopyText, isPro]);
809
+
810
+ // Generate formatted output for copying/preview - with collapsible support
811
+ const renderPreviewContent = () => {
812
+ const dataSource = hasLiveData ? events : [mockRequestData];
813
+ const requestCount = dataSource.length;
814
+
815
+ // Collapsed state - show summary with expand button
816
+ if (!isPreviewExpanded) {
817
+ const warningColor = sizeWarningLevel === "danger" ? macOSColors.semantic.error : sizeWarningLevel === "warning" ? macOSColors.semantic.warning : macOSColors.text.secondary;
818
+ return /*#__PURE__*/_jsxs("div", {
819
+ style: styles.collapsedPreview,
820
+ children: [/*#__PURE__*/_jsx("button", {
821
+ type: "button",
822
+ style: styles.expandButton,
823
+ onClick: () => setIsPreviewExpanded(true),
824
+ children: /*#__PURE__*/_jsxs("span", {
825
+ style: styles.expandButtonContent,
826
+ children: [/*#__PURE__*/_jsx(Eye, {
827
+ size: 16,
828
+ color: macOSColors.semantic.info
829
+ }), /*#__PURE__*/_jsxs("span", {
830
+ style: styles.expandButtonTextContainer,
831
+ children: [/*#__PURE__*/_jsx("span", {
832
+ style: styles.expandButtonTitle,
833
+ children: "Show Preview"
834
+ }), /*#__PURE__*/_jsxs("span", {
835
+ style: styles.expandButtonSubtitle,
836
+ children: [requestCount, " request", requestCount !== 1 ? "s" : "", " \u2022 ~", formatBytes(estimatedSize)]
837
+ })]
838
+ }), /*#__PURE__*/_jsx(ChevronDown, {
839
+ size: 16,
840
+ color: macOSColors.text.muted
841
+ })]
842
+ })
843
+ }), sizeWarningLevel !== "none" && /*#__PURE__*/_jsxs("div", {
844
+ style: {
845
+ ...styles.sizeWarning,
846
+ ...(sizeWarningLevel === "danger" ? styles.sizeWarningDanger : null)
847
+ },
848
+ children: [/*#__PURE__*/_jsx(AlertTriangle, {
849
+ size: 12,
850
+ color: warningColor
851
+ }), /*#__PURE__*/_jsx("span", {
852
+ style: {
853
+ ...styles.sizeWarningText,
854
+ color: warningColor
855
+ },
856
+ children: sizeWarningLevel === "danger" ? "Very large payload - may cause performance issues" : "Large payload - preview may be slow"
857
+ })]
858
+ })]
859
+ });
860
+ }
861
+
862
+ // Expanded state - show collapse button + actual content
863
+ return /*#__PURE__*/_jsxs("div", {
864
+ children: [/*#__PURE__*/_jsxs("button", {
865
+ type: "button",
866
+ style: styles.collapseButton,
867
+ onClick: () => setIsPreviewExpanded(false),
868
+ children: [/*#__PURE__*/_jsx("span", {
869
+ style: styles.collapseButtonText,
870
+ children: "Hide Preview"
871
+ }), /*#__PURE__*/_jsx(ChevronUp, {
872
+ size: 14,
873
+ color: macOSColors.text.secondary
874
+ })]
875
+ }), generateExpandedPreviewContent()]
876
+ });
877
+ };
878
+
879
+ // Preview header actions (status label and Copy button)
880
+ const statusLabel = hasLiveData && lastEventTimestamp ? `Live data • Updated ${formatRelativeTime(lastEventTimestamp, tick)}` : "Mock data (no events captured)";
881
+
882
+ // Section configs — ids/labels/icons/colors verbatim from the native
883
+ // `dynamicFilterConfig` memo.
884
+ const presetOptions = [{
885
+ id: "preset::urls",
886
+ label: "URLs",
887
+ icon: Link,
888
+ color: macOSColors.semantic.info,
889
+ value: "urls",
890
+ isActive: activePreset === "urls"
891
+ }, {
892
+ id: "preset::llm",
893
+ label: "LLM",
894
+ icon: Zap,
895
+ color: macOSColors.semantic.success,
896
+ value: "llm",
897
+ isActive: activePreset === "llm"
898
+ }, {
899
+ id: "preset::json",
900
+ label: "JSON",
901
+ icon: FileCode,
902
+ color: macOSColors.semantic.warning,
903
+ value: "json",
904
+ isActive: activePreset === "json"
905
+ }, {
906
+ id: "preset::full",
907
+ label: "Full",
908
+ icon: FileText,
909
+ color: macOSColors.semantic.info,
910
+ value: "full",
911
+ isActive: activePreset === "full"
912
+ }, {
913
+ id: "preset::custom",
914
+ label: "Custom",
915
+ icon: Settings,
916
+ color: macOSColors.text.secondary,
917
+ value: "custom",
918
+ isActive: activePreset === null
919
+ }];
920
+ const includeOptions = [{
921
+ id: "requestInfo::includeMethod",
922
+ label: "URL",
923
+ value: "includeMethod",
924
+ isActive: settings.includeMethod
925
+ }, {
926
+ id: "requestInfo::includeStatus",
927
+ label: "Status",
928
+ value: "includeStatus",
929
+ isActive: settings.includeStatus
930
+ }, {
931
+ id: "requestInfo::includeTimestamp",
932
+ label: "Time",
933
+ value: "includeTimestamp",
934
+ isActive: settings.includeTimestamp
935
+ }, {
936
+ id: "headers::includeRequestHeaders",
937
+ label: "Headers",
938
+ value: "includeRequestHeaders",
939
+ isActive: settings.includeRequestHeaders
940
+ }, {
941
+ id: "body::includeRequestBody",
942
+ label: "Req Body",
943
+ value: "includeRequestBody",
944
+ isActive: settings.includeRequestBody
945
+ }, {
946
+ id: "body::includeResponseBody",
947
+ label: "Res Body",
948
+ value: "includeResponseBody",
949
+ isActive: settings.includeResponseBody
950
+ }, {
951
+ id: "requestInfo::includeErrors",
952
+ label: "Errors",
953
+ value: "includeErrors",
954
+ isActive: settings.includeErrors
955
+ }];
956
+ const formatOptions = [{
957
+ id: "format::markdown",
958
+ label: "Markdown",
959
+ icon: FileText,
960
+ color: macOSColors.semantic.info,
961
+ value: "markdown",
962
+ isActive: settings.format === "markdown"
963
+ }, {
964
+ id: "format::json",
965
+ label: "JSON",
966
+ icon: FileCode,
967
+ color: macOSColors.semantic.warning,
968
+ value: "json",
969
+ isActive: settings.format === "json"
970
+ }, {
971
+ id: "format::plaintext",
972
+ label: "Text",
973
+ icon: Hash,
974
+ color: macOSColors.text.secondary,
975
+ value: "plaintext",
976
+ isActive: settings.format === "plaintext"
977
+ }];
978
+
979
+ // The DynamicFilterView subtree for this config: sections, then PREVIEW.
980
+ return /*#__PURE__*/_jsx("div", {
981
+ style: styles.container,
982
+ children: /*#__PURE__*/_jsxs("div", {
983
+ style: styles.scrollContent,
984
+ children: [/*#__PURE__*/_jsx(FilterCardSection, {
985
+ title: "Presets",
986
+ options: presetOptions,
987
+ onOptionPress: handleOptionChange
988
+ }), /*#__PURE__*/_jsx(FilterCardSection, {
989
+ title: "Include",
990
+ options: includeOptions,
991
+ onOptionPress: handleOptionChange
992
+ }), /*#__PURE__*/_jsx(FilterCardSection, {
993
+ title: "Format",
994
+ options: formatOptions,
995
+ onOptionPress: handleOptionChange
996
+ }), /*#__PURE__*/_jsxs("div", {
997
+ style: styles.previewSection,
998
+ children: [/*#__PURE__*/_jsx(SectionHeaderRow, {
999
+ icon: Eye,
1000
+ title: "PREVIEW",
1001
+ actions: /*#__PURE__*/_jsxs(_Fragment, {
1002
+ children: [/*#__PURE__*/_jsx("span", {
1003
+ style: styles.statusLabel,
1004
+ children: statusLabel
1005
+ }), /*#__PURE__*/_jsx(CopyButton, {
1006
+ value: generateCopyText(),
1007
+ size: 14,
1008
+ "aria-label": "Copy preview"
1009
+ })]
1010
+ })
1011
+ }), /*#__PURE__*/_jsx("div", {
1012
+ style: styles.previewContent,
1013
+ children: renderPreviewContent()
1014
+ })]
1015
+ })]
1016
+ })
1017
+ });
1018
+ }
1019
+
1020
+ // Values mirror the native StyleSheet blocks (NetworkCopySettingsView.tsx +
1021
+ // DynamicFilterView.tsx + SectionHeader.tsx) 1:1.
1022
+ const styles = {
1023
+ // DynamicFilterView `container` + `scrollContent`.
1024
+ container: {
1025
+ display: "flex",
1026
+ flexDirection: "column",
1027
+ flex: 1,
1028
+ minHeight: 0,
1029
+ backgroundColor: buoyColors.base,
1030
+ overflowY: "auto"
1031
+ },
1032
+ scrollContent: {
1033
+ padding: "16px 16px 24px"
1034
+ },
1035
+ // DynamicFilterView `section` + `filterGrid` + cards.
1036
+ section: {
1037
+ marginBottom: 8
1038
+ },
1039
+ filterGrid: {
1040
+ display: "flex",
1041
+ flexDirection: "row",
1042
+ flexWrap: "wrap",
1043
+ gap: 8,
1044
+ marginTop: 8
1045
+ },
1046
+ filterCard: {
1047
+ display: "flex",
1048
+ backgroundColor: buoyColors.card,
1049
+ borderRadius: 12,
1050
+ padding: "10px 8px",
1051
+ flexDirection: "column",
1052
+ alignItems: "center",
1053
+ justifyContent: "center",
1054
+ border: `1px solid ${buoyColors.border}`,
1055
+ width: 72,
1056
+ minHeight: 68,
1057
+ boxSizing: "border-box",
1058
+ cursor: "pointer",
1059
+ margin: 0,
1060
+ fontFamily: "inherit",
1061
+ gap: 0
1062
+ },
1063
+ activeFilterCard: {
1064
+ backgroundColor: `${buoyColors.primary}33`,
1065
+ border: `2px solid ${buoyColors.primary}`,
1066
+ // Native: shadowColor primary, opacity 0.3, radius 8.
1067
+ boxShadow: `0 0 8px ${buoyColors.primary}4D`
1068
+ },
1069
+ filterLabelCompact: {
1070
+ fontSize: 10,
1071
+ color: buoyColors.text,
1072
+ fontWeight: 600,
1073
+ textAlign: "center",
1074
+ textTransform: "capitalize",
1075
+ // Native textShadow 0/0.5/1 @ 0.3 black.
1076
+ textShadow: "0 0.5px 1px rgba(0, 0, 0, 0.3)",
1077
+ marginTop: 3
1078
+ },
1079
+ // SectionHeader.tsx values.
1080
+ sectionHeader: {
1081
+ display: "flex",
1082
+ flexDirection: "row",
1083
+ alignItems: "center",
1084
+ padding: "8px 16px",
1085
+ backgroundColor: buoyColors.base,
1086
+ minHeight: 40,
1087
+ boxSizing: "border-box"
1088
+ },
1089
+ sectionHeaderIcon: {
1090
+ display: "inline-flex",
1091
+ marginRight: 8
1092
+ },
1093
+ sectionHeaderTitle: {
1094
+ flex: 1,
1095
+ minWidth: 0,
1096
+ fontSize: 14,
1097
+ fontWeight: 600,
1098
+ color: buoyColors.text,
1099
+ whiteSpace: "nowrap",
1100
+ overflow: "hidden",
1101
+ textOverflow: "ellipsis",
1102
+ textAlign: "left"
1103
+ },
1104
+ sectionHeaderActions: {
1105
+ display: "inline-flex",
1106
+ flexDirection: "row",
1107
+ alignItems: "center",
1108
+ gap: 8,
1109
+ marginLeft: "auto"
1110
+ },
1111
+ // DynamicFilterView `previewSection`/`previewContent`.
1112
+ previewSection: {
1113
+ backgroundColor: buoyColors.hover,
1114
+ borderRadius: 16,
1115
+ border: `1px solid ${buoyColors.borderStrong}`,
1116
+ marginTop: 16,
1117
+ overflow: "hidden"
1118
+ },
1119
+ previewContent: {
1120
+ padding: "12px 16px 16px",
1121
+ backgroundColor: buoyColors.base,
1122
+ margin: "0 12px 12px",
1123
+ borderRadius: 8,
1124
+ border: `1px solid ${buoyColors.border}`
1125
+ },
1126
+ // NetworkCopySettingsView.tsx values.
1127
+ previewScroll: {
1128
+ maxHeight: 300,
1129
+ overflowY: "auto"
1130
+ },
1131
+ markdownText: {
1132
+ fontFamily: "ui-monospace, Menlo, monospace",
1133
+ fontSize: 11,
1134
+ color: macOSColors.text.primary,
1135
+ lineHeight: "18px",
1136
+ whiteSpace: "pre-wrap",
1137
+ wordBreak: "break-word",
1138
+ userSelect: "text"
1139
+ },
1140
+ plaintextText: {
1141
+ fontFamily: "ui-monospace, Menlo, monospace",
1142
+ fontSize: 11,
1143
+ color: macOSColors.text.primary,
1144
+ lineHeight: "18px",
1145
+ whiteSpace: "pre-wrap",
1146
+ wordBreak: "break-word",
1147
+ userSelect: "text"
1148
+ },
1149
+ statusLabel: {
1150
+ fontSize: 11,
1151
+ fontWeight: 600,
1152
+ color: macOSColors.text.muted,
1153
+ marginRight: 8
1154
+ },
1155
+ // Collapsible preview styles
1156
+ collapsedPreview: {
1157
+ display: "flex",
1158
+ flexDirection: "column",
1159
+ gap: 8
1160
+ },
1161
+ expandButton: {
1162
+ display: "block",
1163
+ width: "100%",
1164
+ boxSizing: "border-box",
1165
+ backgroundColor: macOSColors.background.input,
1166
+ borderRadius: 8,
1167
+ border: `1px solid ${macOSColors.border.default}`,
1168
+ padding: 12,
1169
+ cursor: "pointer",
1170
+ margin: 0,
1171
+ fontFamily: "inherit",
1172
+ textAlign: "left"
1173
+ },
1174
+ expandButtonContent: {
1175
+ display: "flex",
1176
+ flexDirection: "row",
1177
+ alignItems: "center",
1178
+ gap: 10
1179
+ },
1180
+ expandButtonTextContainer: {
1181
+ display: "flex",
1182
+ flexDirection: "column",
1183
+ flex: 1,
1184
+ minWidth: 0
1185
+ },
1186
+ expandButtonTitle: {
1187
+ fontSize: 13,
1188
+ fontWeight: 600,
1189
+ color: macOSColors.text.primary
1190
+ },
1191
+ expandButtonSubtitle: {
1192
+ fontSize: 11,
1193
+ color: macOSColors.text.secondary,
1194
+ marginTop: 2
1195
+ },
1196
+ collapseButton: {
1197
+ display: "flex",
1198
+ width: "100%",
1199
+ boxSizing: "border-box",
1200
+ flexDirection: "row",
1201
+ alignItems: "center",
1202
+ justifyContent: "center",
1203
+ gap: 6,
1204
+ padding: "8px 0",
1205
+ margin: "0 0 8px",
1206
+ backgroundColor: macOSColors.background.hover,
1207
+ borderRadius: 6,
1208
+ border: `1px solid ${macOSColors.border.default}`,
1209
+ cursor: "pointer",
1210
+ fontFamily: "inherit"
1211
+ },
1212
+ collapseButtonText: {
1213
+ fontSize: 12,
1214
+ fontWeight: 500,
1215
+ color: macOSColors.text.secondary
1216
+ },
1217
+ sizeWarning: {
1218
+ display: "flex",
1219
+ flexDirection: "row",
1220
+ alignItems: "center",
1221
+ gap: 6,
1222
+ padding: "6px 10px",
1223
+ backgroundColor: macOSColors.semantic.warningBackground,
1224
+ borderRadius: 6,
1225
+ border: `1px solid ${macOSColors.semantic.warning}30`
1226
+ },
1227
+ sizeWarningDanger: {
1228
+ backgroundColor: macOSColors.semantic.errorBackground,
1229
+ border: `1px solid ${macOSColors.semantic.error}30`
1230
+ },
1231
+ sizeWarningText: {
1232
+ fontSize: 11,
1233
+ fontWeight: 500,
1234
+ flex: 1,
1235
+ textAlign: "left"
1236
+ }
1237
+ };