@buoy-gg/network 7.0.14 → 7.0.16

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,598 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NetworkHeaderLive.web — DOM twin of NetworkHeaderLive.tsx.
5
+ *
6
+ * The header pieces that consume LIVE event data, each its own narrow store
7
+ * subscriber (same decomposition + hooks as native — `useNetworkBadgeCounts`,
8
+ * `useNetworkHasEvents`, `useNetworkSaved` are platform-free and reused, not
9
+ * forked):
10
+ *
11
+ * - `NetworkHeaderBadges` — success/error/pending count chips (tap toggles
12
+ * the status filter)
13
+ * - `NetworkClearButton` — trash button, enabled while the store has events
14
+ * - `NetworkSavedButton` — Saved-screen entry point with its live count
15
+ * - `NetworkDetailPinActions` — pin + save toggles for the request-DETAIL
16
+ * header (ported with the F3 detail-view feature; same flagsForEventId /
17
+ * cap logic and press-time event resolution as native)
18
+ * - `NetworkHeaderCopyButton` — copy-all button / labelled ⋯-menu row
19
+ * (ported with the F5 saved+copy feature; the copy text is the SAME
20
+ * platform-free `generateNetworkCopyText`, resolved lazily at press time
21
+ * from a live store read, exactly like native)
22
+ *
23
+ * shared-ui RN chrome is replicated as DOM with provenance (web consumers
24
+ * resolve `@buoy-gg/shared-ui` to a minimal shim):
25
+ * - `macOSColors` — inlined verbatim subset from
26
+ * shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts.
27
+ * - Icons — inline lucide SVGs (shared-ui's icons are lucide re-exports).
28
+ * - The active chip's native shadow (shadowOffset 0/1, opacity 0.08,
29
+ * radius 2) → an equivalent CSS box-shadow.
30
+ * - shared-ui `CopyButton` (the Pro branch of the copy button) — inlined
31
+ * labelled DOM twin below: same idle/success/error icon set, 1500ms
32
+ * feedback, lazy function value, and label-follows-labelStyle behavior.
33
+ *
34
+ * Intentional deviations (all logged):
35
+ * - shared-ui CopyButton's ClipboardHintBanner and internal
36
+ * @buoy-gg/license Pro modal are not ported — license gating isn't part
37
+ * of the web port; the explicit `isPro` prop branch (native's own outer
38
+ * gate) still routes free users to `onUpgradePress`.
39
+ * - `CopySettings` is imported from the platform-free
40
+ * `generateNetworkCopyText` module (the native file imports it from
41
+ * NetworkCopySettingsView.tsx, which would drag react-native in).
42
+ *
43
+ * @platform React DOM Web
44
+ */
45
+
46
+ import { useEffect, useRef, useState } from "react";
47
+ import { networkEventStore } from "../utils/networkEventStore";
48
+ import { flagsForEventId, networkSavedStore, FREE_TIER_SAVED_LIMIT, MAX_PINNED, MAX_SAVED } from "../utils/networkSavedStore";
49
+ import { useNetworkSaved } from "../hooks/useNetworkSaved";
50
+ import { useSelectedNetworkEventId } from "../hooks/useNetworkSelection";
51
+ import { useNetworkBadgeCounts } from "../hooks/useNetworkBadgeCounts";
52
+ import { useNetworkHasEvents, useNetworkHasMatches } from "../hooks/useNetworkHasMatches";
53
+ import { generateNetworkCopyText } from "../utils/generateNetworkCopyText";
54
+ import { applyIgnoredPatterns, filterNetworkEvents } from "../utils/filterNetworkEvents";
55
+
56
+ // =============================
57
+ // Native palette (provenance)
58
+ // =============================
59
+
60
+ /** macOSColors subset — shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts, verbatim. */
61
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
62
+ const macOSColors = {
63
+ background: {
64
+ hover: "#1D1D1F"
65
+ },
66
+ border: {
67
+ default: "#2D2D2F"
68
+ },
69
+ text: {
70
+ primary: "#F5F5F7",
71
+ secondary: "#A1A1A6",
72
+ muted: "#8E8E93",
73
+ disabled: "#9E9EA0"
74
+ },
75
+ semantic: {
76
+ success: "#34C759",
77
+ error: "#FF453A",
78
+ warning: "#FFEB3B",
79
+ info: "#00B8E6",
80
+ infoBackground: "rgba(0, 184, 230, 0.1)",
81
+ debug: "#BF5AF2"
82
+ }
83
+ };
84
+
85
+ // =============================
86
+ // Inline lucide icons (shared-ui icons are lucide re-exports)
87
+ // =============================
88
+
89
+ function Svg({
90
+ size = 14,
91
+ color = "currentColor",
92
+ children
93
+ }) {
94
+ return /*#__PURE__*/_jsx("svg", {
95
+ width: size,
96
+ height: size,
97
+ viewBox: "0 0 24 24",
98
+ fill: "none",
99
+ stroke: color,
100
+ strokeWidth: 2,
101
+ strokeLinecap: "round",
102
+ strokeLinejoin: "round",
103
+ style: {
104
+ flexShrink: 0,
105
+ display: "block"
106
+ },
107
+ children: children
108
+ });
109
+ }
110
+ const CheckCircle = p => /*#__PURE__*/_jsxs(Svg, {
111
+ ...p,
112
+ children: [/*#__PURE__*/_jsx("path", {
113
+ d: "M21.801 10A10 10 0 1 1 17 3.335"
114
+ }), /*#__PURE__*/_jsx("path", {
115
+ d: "m9 11 3 3L22 4"
116
+ })]
117
+ });
118
+ const XCircle = p => /*#__PURE__*/_jsxs(Svg, {
119
+ ...p,
120
+ children: [/*#__PURE__*/_jsx("circle", {
121
+ cx: "12",
122
+ cy: "12",
123
+ r: "10"
124
+ }), /*#__PURE__*/_jsx("path", {
125
+ d: "m15 9-6 6"
126
+ }), /*#__PURE__*/_jsx("path", {
127
+ d: "m9 9 6 6"
128
+ })]
129
+ });
130
+ const Clock = p => /*#__PURE__*/_jsxs(Svg, {
131
+ ...p,
132
+ children: [/*#__PURE__*/_jsx("circle", {
133
+ cx: "12",
134
+ cy: "12",
135
+ r: "10"
136
+ }), /*#__PURE__*/_jsx("polyline", {
137
+ points: "12 6 12 12 16 14"
138
+ })]
139
+ });
140
+ const Trash2 = p => /*#__PURE__*/_jsxs(Svg, {
141
+ ...p,
142
+ children: [/*#__PURE__*/_jsx("path", {
143
+ d: "M3 6h18"
144
+ }), /*#__PURE__*/_jsx("path", {
145
+ d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"
146
+ }), /*#__PURE__*/_jsx("path", {
147
+ d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
148
+ }), /*#__PURE__*/_jsx("line", {
149
+ x1: "10",
150
+ x2: "10",
151
+ y1: "11",
152
+ y2: "17"
153
+ }), /*#__PURE__*/_jsx("line", {
154
+ x1: "14",
155
+ x2: "14",
156
+ y1: "11",
157
+ y2: "17"
158
+ })]
159
+ });
160
+ const Bookmark = p => /*#__PURE__*/_jsx(Svg, {
161
+ ...p,
162
+ children: /*#__PURE__*/_jsx("path", {
163
+ d: "m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"
164
+ })
165
+ });
166
+ const Pin = p => /*#__PURE__*/_jsxs(Svg, {
167
+ ...p,
168
+ children: [/*#__PURE__*/_jsx("path", {
169
+ d: "M12 17v5"
170
+ }), /*#__PURE__*/_jsx("path", {
171
+ d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1z"
172
+ })]
173
+ });
174
+ const Copy = p => /*#__PURE__*/_jsxs(Svg, {
175
+ ...p,
176
+ children: [/*#__PURE__*/_jsx("rect", {
177
+ width: "14",
178
+ height: "14",
179
+ x: "8",
180
+ y: "8",
181
+ rx: "2",
182
+ ry: "2"
183
+ }), /*#__PURE__*/_jsx("path", {
184
+ d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"
185
+ })]
186
+ });
187
+ const CheckCircle2 = p => /*#__PURE__*/_jsxs(Svg, {
188
+ ...p,
189
+ children: [/*#__PURE__*/_jsx("circle", {
190
+ cx: "12",
191
+ cy: "12",
192
+ r: "10"
193
+ }), /*#__PURE__*/_jsx("path", {
194
+ d: "m9 12 2 2 4-4"
195
+ })]
196
+ });
197
+ const AlertTriangle = p => /*#__PURE__*/_jsxs(Svg, {
198
+ ...p,
199
+ children: [/*#__PURE__*/_jsx("path", {
200
+ 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"
201
+ }), /*#__PURE__*/_jsx("path", {
202
+ d: "M12 9v4"
203
+ }), /*#__PURE__*/_jsx("path", {
204
+ d: "M12 17h.01"
205
+ })]
206
+ });
207
+ /** Success / error / pending chips — tap to toggle the status filter. */
208
+ export function NetworkHeaderBadges({
209
+ activeStatus,
210
+ onToggleStatus,
211
+ ignoredPatterns,
212
+ paused
213
+ }) {
214
+ const counts = useNetworkBadgeCounts({
215
+ ignoredPatterns,
216
+ paused
217
+ });
218
+ return /*#__PURE__*/_jsxs("div", {
219
+ style: styles.headerChipRow,
220
+ children: [/*#__PURE__*/_jsxs("button", {
221
+ type: "button",
222
+ style: {
223
+ ...styles.headerChip,
224
+ ...(activeStatus === "success" ? styles.headerChipActive : null)
225
+ },
226
+ onClick: () => onToggleStatus("success"),
227
+ children: [/*#__PURE__*/_jsx(CheckCircle, {
228
+ size: 12,
229
+ color: macOSColors.semantic.success
230
+ }), /*#__PURE__*/_jsx("span", {
231
+ style: {
232
+ ...styles.headerChipValue,
233
+ color: macOSColors.semantic.success
234
+ },
235
+ children: counts.successful
236
+ })]
237
+ }), /*#__PURE__*/_jsxs("button", {
238
+ type: "button",
239
+ style: {
240
+ ...styles.headerChip,
241
+ ...(activeStatus === "error" ? styles.headerChipActive : null)
242
+ },
243
+ onClick: () => onToggleStatus("error"),
244
+ children: [/*#__PURE__*/_jsx(XCircle, {
245
+ size: 12,
246
+ color: macOSColors.semantic.error
247
+ }), /*#__PURE__*/_jsx("span", {
248
+ style: {
249
+ ...styles.headerChipValue,
250
+ color: macOSColors.semantic.error
251
+ },
252
+ children: counts.failed
253
+ })]
254
+ }), /*#__PURE__*/_jsxs("button", {
255
+ type: "button",
256
+ style: {
257
+ ...styles.headerChip,
258
+ ...(activeStatus === "pending" ? styles.headerChipActive : null)
259
+ },
260
+ onClick: () => onToggleStatus("pending"),
261
+ children: [/*#__PURE__*/_jsx(Clock, {
262
+ size: 12,
263
+ color: macOSColors.semantic.warning
264
+ }), /*#__PURE__*/_jsx("span", {
265
+ style: {
266
+ ...styles.headerChipValue,
267
+ color: macOSColors.semantic.warning
268
+ },
269
+ children: counts.pending
270
+ })]
271
+ })]
272
+ });
273
+ }
274
+ /** Trash button — handler co-located; enabled while the store has events.
275
+ * Subscribes to a flip-guarded boolean, so it renders when the store goes
276
+ * empty↔non-empty, not on every count change. */
277
+ export function NetworkClearButton({
278
+ paused
279
+ }) {
280
+ const hasEvents = useNetworkHasEvents({
281
+ paused
282
+ });
283
+ return /*#__PURE__*/_jsx("button", {
284
+ type: "button",
285
+ "aria-label": "Clear events",
286
+ onClick: () => networkEventStore.clearEvents(),
287
+ style: {
288
+ ...styles.headerActionButton,
289
+ ...(!hasEvents ? styles.headerActionButtonDisabled : null)
290
+ },
291
+ disabled: !hasEvents,
292
+ children: /*#__PURE__*/_jsx(Trash2, {
293
+ size: 14,
294
+ color: hasEvents ? macOSColors.text.muted : macOSColors.text.disabled
295
+ })
296
+ });
297
+ }
298
+ /**
299
+ * Entry point to the Saved screen, with its count. A self-subscribing leaf so
300
+ * the badge tracks the store without the modal header re-rendering.
301
+ */
302
+ export function NetworkSavedButton({
303
+ onPress
304
+ }) {
305
+ const {
306
+ savedRecords
307
+ } = useNetworkSaved();
308
+ const count = savedRecords.length;
309
+ return /*#__PURE__*/_jsxs("button", {
310
+ type: "button",
311
+ onClick: onPress,
312
+ "aria-label": "Saved requests",
313
+ style: styles.headerActionButton,
314
+ children: [/*#__PURE__*/_jsx(Bookmark, {
315
+ size: 14,
316
+ color: count > 0 ? macOSColors.semantic.debug : macOSColors.text.muted
317
+ }), count > 0 ? /*#__PURE__*/_jsx("span", {
318
+ style: styles.savedBadge,
319
+ children: /*#__PURE__*/_jsx("span", {
320
+ style: styles.savedBadgeText,
321
+ children: count > 99 ? "99+" : count
322
+ })
323
+ }) : null]
324
+ });
325
+ }
326
+ /**
327
+ * Pin + Save toggles for the request-detail header — same self-subscribing
328
+ * leaf as native, for the same two reasons: the host root stays free of
329
+ * subscriptions, and these buttons deliberately do NOT live inside
330
+ * `NetworkEventDetailView` (the Events tool renders that same detail view
331
+ * with no saved store behind it).
332
+ *
333
+ * The event object is resolved at PRESS time (live store first, then the
334
+ * saved snapshot), and the selected id is SUBSCRIBED here rather than passed
335
+ * down, so stepping through requests doesn't rebuild the host header.
336
+ */
337
+ export function NetworkDetailPinActions({
338
+ isPro,
339
+ onUpgradePress
340
+ }) {
341
+ const eventId = useSelectedNetworkEventId();
342
+ const state = useNetworkSaved();
343
+ const {
344
+ pinned,
345
+ saved
346
+ } = flagsForEventId(state, eventId);
347
+ const savedLimit = isPro ? MAX_SAVED : FREE_TIER_SAVED_LIMIT;
348
+ const atPinCap = !pinned && state.pinnedEvents.length >= MAX_PINNED;
349
+ const atSavedCap = !saved && state.savedRecords.length >= savedLimit;
350
+ const resolveEvent = () => {
351
+ if (!eventId) return null;
352
+ return networkEventStore.getEvents().find(event => event.id === eventId) ?? networkSavedStore.getEventById(eventId);
353
+ };
354
+ const handlePin = () => {
355
+ const event = resolveEvent();
356
+ if (event) state.togglePin(event);
357
+ };
358
+ const handleSave = () => {
359
+ // Hitting the free ceiling is an upgrade moment, not a dead button.
360
+ if (atSavedCap && !isPro) {
361
+ onUpgradePress();
362
+ return;
363
+ }
364
+ const event = resolveEvent();
365
+ if (event) state.toggleSave(event);
366
+ };
367
+ return /*#__PURE__*/_jsxs(_Fragment, {
368
+ children: [/*#__PURE__*/_jsx("button", {
369
+ type: "button",
370
+ onClick: handlePin,
371
+ disabled: atPinCap,
372
+ "aria-label": pinned ? "Unpin request" : "Pin request",
373
+ title: atPinCap ? `Pin limit reached (${MAX_PINNED})` : undefined,
374
+ style: {
375
+ ...styles.headerActionButton,
376
+ ...(pinned ? styles.headerActionButtonActive : null),
377
+ ...(atPinCap ? styles.headerActionButtonDisabled : null)
378
+ },
379
+ children: /*#__PURE__*/_jsx(Pin, {
380
+ size: 14,
381
+ color: pinned ? macOSColors.semantic.info : atPinCap ? macOSColors.text.disabled : macOSColors.text.secondary
382
+ })
383
+ }), /*#__PURE__*/_jsx("button", {
384
+ type: "button",
385
+ onClick: handleSave,
386
+ disabled: atSavedCap && isPro,
387
+ "aria-label": saved ? "Remove from saved" : "Save request",
388
+ title: atSavedCap && isPro ? `Saved limit reached (${MAX_SAVED})` : undefined,
389
+ style: {
390
+ ...styles.headerActionButton,
391
+ ...(saved ? styles.headerActionButtonSaved : null),
392
+ ...(atSavedCap && isPro ? styles.headerActionButtonDisabled : null)
393
+ },
394
+ children: /*#__PURE__*/_jsx(Bookmark, {
395
+ size: 14,
396
+ color: saved ? macOSColors.semantic.debug : atSavedCap && isPro ? macOSColors.text.disabled : macOSColors.text.secondary
397
+ })
398
+ })]
399
+ });
400
+ }
401
+ /**
402
+ * Copy-all-requests button. Subscribes to a flip-guarded boolean (its
403
+ * enabled state); the copy text is generated at PRESS time from a live
404
+ * store read, so there's no per-event work and no stale closure.
405
+ *
406
+ * The Pro branch inlines shared-ui CopyButton's copy/feedback loop (same
407
+ * icon set, 1500ms feedback, label keeps its own color while the icon
408
+ * follows the state — native's `labelStyle` wins over the state color).
409
+ */
410
+ export function NetworkHeaderCopyButton({
411
+ isPro,
412
+ filter,
413
+ ignoredPatterns,
414
+ copySettings,
415
+ paused,
416
+ onUpgradePress,
417
+ label
418
+ }) {
419
+ const hasEvents = useNetworkHasMatches({
420
+ filter,
421
+ ignoredPatterns,
422
+ paused
423
+ });
424
+ const [copyState, setCopyState] = useState("idle");
425
+ const timeoutRef = useRef(null);
426
+ useEffect(() => () => {
427
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
428
+ }, []);
429
+ if (!isPro) {
430
+ return /*#__PURE__*/_jsxs("button", {
431
+ type: "button",
432
+ onClick: onUpgradePress,
433
+ style: {
434
+ ...(label ? styles.copyMenuRow : styles.headerActionButton),
435
+ ...(!hasEvents ? styles.headerActionButtonDisabled : null)
436
+ },
437
+ disabled: !hasEvents,
438
+ "aria-label": label ?? "Copy requests",
439
+ children: [/*#__PURE__*/_jsx(Copy, {
440
+ size: 14,
441
+ color: hasEvents ? macOSColors.text.secondary : macOSColors.text.disabled
442
+ }), label ? /*#__PURE__*/_jsx("span", {
443
+ style: styles.copyMenuLabel,
444
+ children: label
445
+ }) : null]
446
+ });
447
+ }
448
+ const settle = state => {
449
+ setCopyState(state);
450
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
451
+ // 1500ms — shared-ui CopyButton's `feedbackDuration` default.
452
+ timeoutRef.current = setTimeout(() => {
453
+ setCopyState("idle");
454
+ timeoutRef.current = null;
455
+ }, 1500);
456
+ };
457
+ const handleCopy = async () => {
458
+ try {
459
+ // Lazy value: resolved at copy time from the LIVE store — no per-event
460
+ // work, no staleness (native's CopyButton function-value contract).
461
+ const text = generateNetworkCopyText(filterNetworkEvents(applyIgnoredPatterns(networkEventStore.getEvents(), ignoredPatterns), filter), copySettings);
462
+ await navigator.clipboard.writeText(text);
463
+ settle("success");
464
+ } catch {
465
+ settle("error");
466
+ }
467
+ };
468
+ const iconColor = copyState === "success" ? macOSColors.semantic.success : copyState === "error" ? macOSColors.semantic.error : hasEvents ? macOSColors.text.secondary : macOSColors.text.disabled;
469
+ return /*#__PURE__*/_jsxs("button", {
470
+ type: "button",
471
+ onClick: copyState === "idle" ? handleCopy : undefined,
472
+ style: {
473
+ ...(label ? styles.copyMenuRow : styles.headerActionButton),
474
+ ...(!hasEvents ? styles.headerActionButtonDisabled : null)
475
+ },
476
+ disabled: !hasEvents,
477
+ "aria-label": copyState === "idle" ? label ?? "Copy to clipboard" : copyState === "success" ? "Copied to clipboard" : "Failed to copy",
478
+ children: [copyState === "success" ? /*#__PURE__*/_jsx(CheckCircle2, {
479
+ size: 14,
480
+ color: iconColor
481
+ }) : copyState === "error" ? /*#__PURE__*/_jsx(AlertTriangle, {
482
+ size: 14,
483
+ color: iconColor
484
+ }) : /*#__PURE__*/_jsx(Copy, {
485
+ size: 14,
486
+ color: iconColor
487
+ }), label ? /*#__PURE__*/_jsx("span", {
488
+ style: styles.copyMenuLabel,
489
+ children: label
490
+ }) : null]
491
+ });
492
+ }
493
+
494
+ // Values mirror the native StyleSheet block 1:1.
495
+ const styles = {
496
+ /** The overflow-menu variant: a full-width row, matching the other items. */
497
+ copyMenuRow: {
498
+ display: "flex",
499
+ width: "100%",
500
+ boxSizing: "border-box",
501
+ flexDirection: "row",
502
+ alignItems: "center",
503
+ gap: 10,
504
+ padding: "11px 6px",
505
+ justifyContent: "flex-start",
506
+ background: "none",
507
+ border: "none",
508
+ cursor: "pointer",
509
+ margin: 0,
510
+ fontFamily: "inherit",
511
+ textAlign: "left"
512
+ },
513
+ copyMenuLabel: {
514
+ fontSize: 13,
515
+ fontWeight: 600,
516
+ color: macOSColors.text.primary
517
+ },
518
+ headerChipRow: {
519
+ display: "flex",
520
+ flexDirection: "row",
521
+ alignItems: "center",
522
+ gap: 8
523
+ },
524
+ /**
525
+ * Counter chips and toolbar buttons carry a permanent outline; the fill
526
+ * marks the active filter.
527
+ */
528
+ headerChip: {
529
+ display: "inline-flex",
530
+ flexDirection: "row",
531
+ alignItems: "center",
532
+ gap: 4,
533
+ backgroundColor: macOSColors.background.hover,
534
+ padding: "5px 10px",
535
+ borderRadius: 12,
536
+ border: `1px solid ${macOSColors.border.default}`,
537
+ cursor: "pointer",
538
+ margin: 0
539
+ },
540
+ headerChipActive: {
541
+ backgroundColor: macOSColors.semantic.infoBackground,
542
+ border: `1px solid ${macOSColors.semantic.info}50`,
543
+ // Native: shadowColor info, offset 0/1, opacity 0.08, radius 2.
544
+ boxShadow: "0 1px 2px rgba(0, 184, 230, 0.08)"
545
+ },
546
+ headerChipValue: {
547
+ fontSize: 12,
548
+ fontWeight: 600,
549
+ fontFamily: "ui-monospace, Menlo, monospace"
550
+ },
551
+ headerActionButton: {
552
+ position: "relative",
553
+ display: "inline-flex",
554
+ width: 32,
555
+ height: 32,
556
+ boxSizing: "border-box",
557
+ borderRadius: 8,
558
+ backgroundColor: macOSColors.background.hover,
559
+ border: `1px solid ${macOSColors.border.default}`,
560
+ alignItems: "center",
561
+ justifyContent: "center",
562
+ cursor: "pointer",
563
+ padding: 0,
564
+ margin: 0
565
+ },
566
+ headerActionButtonDisabled: {
567
+ opacity: 0.55,
568
+ cursor: "default"
569
+ },
570
+ headerActionButtonActive: {
571
+ backgroundColor: macOSColors.semantic.infoBackground,
572
+ border: `1px solid ${macOSColors.semantic.info}50`
573
+ },
574
+ headerActionButtonSaved: {
575
+ backgroundColor: `${macOSColors.semantic.debug}1A`,
576
+ border: `1px solid ${macOSColors.semantic.debug}50`
577
+ },
578
+ savedBadge: {
579
+ position: "absolute",
580
+ top: -3,
581
+ right: -3,
582
+ minWidth: 14,
583
+ height: 14,
584
+ boxSizing: "border-box",
585
+ padding: "0 3px",
586
+ borderRadius: 7,
587
+ backgroundColor: macOSColors.semantic.debug,
588
+ display: "inline-flex",
589
+ alignItems: "center",
590
+ justifyContent: "center"
591
+ },
592
+ savedBadgeText: {
593
+ color: "#FFFFFF",
594
+ fontSize: 9,
595
+ fontWeight: 700,
596
+ fontFamily: "ui-monospace, Menlo, monospace"
597
+ }
598
+ };
@@ -162,7 +162,8 @@ export function NetworkListScreen({
162
162
  } = useNetworkEventList({
163
163
  filter,
164
164
  ignoredPatterns,
165
- paused: paused || !visible,
165
+ paused,
166
+ visible,
166
167
  isPro
167
168
  });
168
169