@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,97 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NetworkDetailScreen.web — DOM twin of the `NetworkDetailScreen` screen
5
+ * component inside NetworkModal.tsx (the native detail screen is private to
6
+ * the modal; web hosts compose their own shell, so the screen is exported
7
+ * here as the reusable piece).
8
+ *
9
+ * Same rule4 contract and the same two-cursor render strategy as native:
10
+ *
11
+ * - Hooks run unconditionally; the component returns null when not visible.
12
+ * - The detail chrome renders at the IMMEDIATE selection
13
+ * (`useSelectedNetworkEventId`) — every step, no delay — while the
14
+ * expensive JSON body trees wait for the settled cursor
15
+ * (`useNetworkBodyEventId`): `showBodies` is false exactly while the
16
+ * prev/next cursor is still moving, so a burst of taps renders the request
17
+ * you land on and none of the ones you flew past.
18
+ * - The ID resolves through `useLiveNetworkEvent` so a request opened while
19
+ * pending picks up its response/error in place.
20
+ * - `reportRendered` still fires after the trees commit — benchmark
21
+ * instrumentation, same gating as native.
22
+ *
23
+ * All hooks and the selection store are the platform-free modules native
24
+ * uses — REUSED, not forked. Layout: the detail body scrolls, the stepper
25
+ * stays pinned under it (the body owns its scroll — see the native comment);
26
+ * the spacer holds the footer down when the ID resolves to nothing.
27
+ *
28
+ * @platform React DOM Web
29
+ */
30
+
31
+ import { useEffect } from "react";
32
+ import { useLiveNetworkEvent } from "../hooks/useLiveNetworkEvent";
33
+ import { useNetworkBodyEventId, useSelectedNetworkEventId } from "../hooks/useNetworkSelection";
34
+ import { networkSelectionStore } from "../utils/networkSelectionStore";
35
+ import { NetworkEventDetailView } from "./NetworkEventDetailView.web";
36
+ import { NetworkDetailStepper } from "./NetworkDetailStepper.web";
37
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
38
+ export function NetworkDetailScreen({
39
+ visible,
40
+ paused,
41
+ ignoredPatternValues,
42
+ onTogglePattern,
43
+ isPro,
44
+ filter,
45
+ ignoredPatterns,
46
+ fromSaved,
47
+ savedSearch,
48
+ applySafeAreaInset
49
+ }) {
50
+ const eventId = useSelectedNetworkEventId();
51
+ const settledId = useNetworkBodyEventId();
52
+ const heavyReady = eventId != null && eventId === settledId;
53
+ // Resolve the ID to the live event so a request opened while pending picks
54
+ // up its response/error in place instead of freezing on the tap snapshot.
55
+ const event = useLiveNetworkEvent(eventId, {
56
+ paused: paused || !visible
57
+ });
58
+
59
+ // Instrumentation for the benchmark harness — reports when the expensive
60
+ // TREES landed (gated on `heavyReady`, same rationale as native).
61
+ useEffect(() => {
62
+ if (visible && event && heavyReady) {
63
+ networkSelectionStore.reportRendered(event.id);
64
+ }
65
+ }, [visible, event, heavyReady]);
66
+ if (!visible) return null;
67
+ // Column: the detail body scrolls, the stepper stays pinned under it.
68
+ return /*#__PURE__*/_jsxs("div", {
69
+ style: {
70
+ display: "flex",
71
+ flexDirection: "column",
72
+ flex: 1,
73
+ minHeight: 0
74
+ },
75
+ children: [event ? /*#__PURE__*/_jsx(NetworkEventDetailView, {
76
+ event: event,
77
+ ignoredPatterns: ignoredPatternValues,
78
+ onTogglePattern: onTogglePattern,
79
+ showBodies: heavyReady
80
+ }) :
81
+ /*#__PURE__*/
82
+ /* Holds the footer down when the selected ID resolves to no event. */
83
+ _jsx("div", {
84
+ style: {
85
+ flex: 1
86
+ }
87
+ }), /*#__PURE__*/_jsx(NetworkDetailStepper, {
88
+ paused: paused,
89
+ filter: filter,
90
+ ignoredPatterns: ignoredPatterns,
91
+ isPro: isPro,
92
+ fromSaved: fromSaved,
93
+ savedSearch: savedSearch,
94
+ applySafeAreaInset: applySafeAreaInset
95
+ })]
96
+ });
97
+ }
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NetworkDetailStepper.web — DOM twin of NetworkDetailStepper.tsx.
5
+ *
6
+ * Previous/Next footer for the request DETAIL screen. The data layer is
7
+ * REUSED, not forked — `useNetworkEventList`, `useNetworkSaved`,
8
+ * `useNetworkCursor`, `networkSelectionStore`, `splitPinned` and
9
+ * `selectSavedEvents` are all platform-free — so the navigable order is
10
+ * EXACTLY what the list screen renders (`[...pinned, ...rest]`), re-scoped in
11
+ * place by the live filter, and detail opened from the Saved screen steps
12
+ * through the saved list instead. It is its own leaf for the same reason as
13
+ * native: it is the only part of the detail screen that needs the event LIST,
14
+ * so a request arriving re-renders this footer and nothing else.
15
+ *
16
+ * shared-ui RN chrome is replicated as DOM with provenance:
17
+ * - `EventStepperFooter` — inlined DOM twin below, values from
18
+ * shared/src/ui/components/EventStepperFooter.tsx (macOSColors verbatim;
19
+ * the native -2px shadow → an equivalent CSS box-shadow).
20
+ * - `applySafeAreaInset` maps `useSafeAreaInsets().bottom` →
21
+ * `env(safe-area-inset-bottom)` (0 on desktop webviews, the real inset on
22
+ * phone webviews).
23
+ *
24
+ * @platform React DOM Web
25
+ */
26
+
27
+ import { memo, useEffect, useMemo } from "react";
28
+ import { useNetworkEventList } from "../hooks/useNetworkEventList";
29
+ import { useNetworkSaved } from "../hooks/useNetworkSaved";
30
+ import { useNetworkCursor } from "../hooks/useNetworkSelection";
31
+ import { networkSelectionStore, toSelectionIds } from "../utils/networkSelectionStore";
32
+ import { splitPinned } from "../utils/withPinnedFirst";
33
+ import { selectSavedEvents } from "../utils/selectSavedEvents";
34
+
35
+ // =============================
36
+ // Native palette (provenance)
37
+ // =============================
38
+
39
+ /** macOSColors subset — shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts, verbatim. */
40
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
41
+ const macOSColors = {
42
+ background: {
43
+ base: "#0A0A0C",
44
+ card: "#1A1A1C"
45
+ },
46
+ border: {
47
+ default: "#2D2D2F"
48
+ },
49
+ text: {
50
+ primary: "#F5F5F7",
51
+ secondary: "#A1A1A6",
52
+ muted: "#8E8E93"
53
+ }
54
+ };
55
+
56
+ // =============================
57
+ // Inline lucide icons (shared-ui icons are lucide re-exports)
58
+ // =============================
59
+
60
+ function Svg({
61
+ size = 14,
62
+ color = "currentColor",
63
+ children
64
+ }) {
65
+ return /*#__PURE__*/_jsx("svg", {
66
+ width: size,
67
+ height: size,
68
+ viewBox: "0 0 24 24",
69
+ fill: "none",
70
+ stroke: color,
71
+ strokeWidth: 2,
72
+ strokeLinecap: "round",
73
+ strokeLinejoin: "round",
74
+ style: {
75
+ flexShrink: 0,
76
+ display: "block"
77
+ },
78
+ children: children
79
+ });
80
+ }
81
+ const ChevronLeft = p => /*#__PURE__*/_jsx(Svg, {
82
+ ...p,
83
+ children: /*#__PURE__*/_jsx("path", {
84
+ d: "m15 18-6-6 6-6"
85
+ })
86
+ });
87
+ const ChevronRight = p => /*#__PURE__*/_jsx(Svg, {
88
+ ...p,
89
+ children: /*#__PURE__*/_jsx("path", {
90
+ d: "m9 18 6-6-6-6"
91
+ })
92
+ });
93
+
94
+ // =============================
95
+ // EventStepperFooter DOM twin (shared/src/ui/components/EventStepperFooter.tsx)
96
+ // =============================
97
+
98
+ const EventStepperFooter = /*#__PURE__*/memo(function EventStepperFooter({
99
+ currentIndex,
100
+ totalItems,
101
+ onPrevious,
102
+ onNext,
103
+ itemLabel = "Event",
104
+ subtitle,
105
+ applySafeAreaInset = true
106
+ }) {
107
+ // Don't render if there's only one or no items (native).
108
+ if (totalItems <= 1) return null;
109
+ const isFirstItem = currentIndex === 0;
110
+ const isLastItem = currentIndex === totalItems - 1;
111
+ return /*#__PURE__*/_jsxs("div", {
112
+ style: {
113
+ ...footerStyles.footer,
114
+ paddingBottom: applySafeAreaInset ? "calc(12px + env(safe-area-inset-bottom, 0px))" : 12
115
+ },
116
+ children: [/*#__PURE__*/_jsxs("button", {
117
+ type: "button",
118
+ onClick: onPrevious,
119
+ disabled: isFirstItem,
120
+ style: {
121
+ ...footerStyles.navButton,
122
+ ...(isFirstItem ? footerStyles.navButtonDisabled : null)
123
+ },
124
+ children: [/*#__PURE__*/_jsx(ChevronLeft, {
125
+ size: 20,
126
+ color: isFirstItem ? macOSColors.text.muted : macOSColors.text.primary
127
+ }), /*#__PURE__*/_jsx("span", {
128
+ style: {
129
+ ...footerStyles.navButtonText,
130
+ ...(isFirstItem ? footerStyles.navButtonTextDisabled : null)
131
+ },
132
+ children: "Previous"
133
+ })]
134
+ }), /*#__PURE__*/_jsxs("div", {
135
+ style: footerStyles.counterContainer,
136
+ children: [/*#__PURE__*/_jsxs("span", {
137
+ style: footerStyles.counterText,
138
+ children: [itemLabel, " ", currentIndex + 1, " of ", totalItems]
139
+ }), subtitle ? /*#__PURE__*/_jsx("span", {
140
+ style: footerStyles.subtitleText,
141
+ children: subtitle
142
+ }) : null]
143
+ }), /*#__PURE__*/_jsxs("button", {
144
+ type: "button",
145
+ onClick: onNext,
146
+ disabled: isLastItem,
147
+ style: {
148
+ ...footerStyles.navButton,
149
+ ...(isLastItem ? footerStyles.navButtonDisabled : null)
150
+ },
151
+ children: [/*#__PURE__*/_jsx("span", {
152
+ style: {
153
+ ...footerStyles.navButtonText,
154
+ ...(isLastItem ? footerStyles.navButtonTextDisabled : null)
155
+ },
156
+ children: "Next"
157
+ }), /*#__PURE__*/_jsx(ChevronRight, {
158
+ size: 20,
159
+ color: isLastItem ? macOSColors.text.muted : macOSColors.text.primary
160
+ })]
161
+ })]
162
+ });
163
+ });
164
+
165
+ // Values mirror the native StyleSheet block 1:1.
166
+ const footerStyles = {
167
+ footer: {
168
+ display: "flex",
169
+ flexDirection: "row",
170
+ alignItems: "center",
171
+ justifyContent: "space-between",
172
+ padding: "12px 16px 12px",
173
+ backgroundColor: macOSColors.background.base,
174
+ borderTop: `1px solid ${macOSColors.border.default}`,
175
+ // Native: shadowOffset 0/-2, opacity 0.1, radius 4.
176
+ boxShadow: "0 -2px 4px rgba(0, 0, 0, 0.1)",
177
+ flexShrink: 0
178
+ },
179
+ navButton: {
180
+ display: "inline-flex",
181
+ flexDirection: "row",
182
+ alignItems: "center",
183
+ gap: 6,
184
+ padding: "8px 12px",
185
+ borderRadius: 6,
186
+ backgroundColor: macOSColors.background.card,
187
+ minWidth: 100,
188
+ boxSizing: "border-box",
189
+ justifyContent: "center",
190
+ border: "none",
191
+ cursor: "pointer",
192
+ margin: 0
193
+ },
194
+ navButtonDisabled: {
195
+ opacity: 0.3,
196
+ cursor: "default"
197
+ },
198
+ navButtonText: {
199
+ fontSize: 12,
200
+ fontWeight: 600,
201
+ color: macOSColors.text.primary,
202
+ fontFamily: "ui-monospace, Menlo, monospace",
203
+ textTransform: "uppercase"
204
+ },
205
+ navButtonTextDisabled: {
206
+ color: macOSColors.text.muted
207
+ },
208
+ counterContainer: {
209
+ display: "flex",
210
+ flexDirection: "column",
211
+ alignItems: "center"
212
+ },
213
+ counterText: {
214
+ fontSize: 14,
215
+ fontWeight: 700,
216
+ color: macOSColors.text.primary,
217
+ fontFamily: "ui-monospace, Menlo, monospace",
218
+ textTransform: "uppercase"
219
+ },
220
+ subtitleText: {
221
+ fontSize: 11,
222
+ color: macOSColors.text.secondary,
223
+ fontFamily: "ui-monospace, Menlo, monospace",
224
+ marginTop: 2
225
+ }
226
+ };
227
+
228
+ // =============================
229
+ // Stepper (logic mirrors the native file 1:1)
230
+ // =============================
231
+
232
+ /** Stable identities — the store is a singleton, so these never need to change. */
233
+ const stepPrevious = () => networkSelectionStore.step(-1);
234
+ const stepNext = () => networkSelectionStore.step(1);
235
+ export const NetworkDetailStepper = /*#__PURE__*/memo(function NetworkDetailStepper({
236
+ paused,
237
+ filter,
238
+ ignoredPatterns,
239
+ isPro,
240
+ fromSaved,
241
+ savedSearch,
242
+ applySafeAreaInset
243
+ }) {
244
+ const {
245
+ visibleEvents
246
+ } = useNetworkEventList({
247
+ filter,
248
+ ignoredPatterns,
249
+ paused,
250
+ isPro
251
+ });
252
+ const {
253
+ pinnedEvents,
254
+ pinnedLiveIds,
255
+ savedRecords
256
+ } = useNetworkSaved();
257
+ const ordered = useMemo(() => {
258
+ if (fromSaved) {
259
+ return selectSavedEvents(savedRecords, isPro, savedSearch).events;
260
+ }
261
+ // Same split the list screen does — pins render above the list, so they
262
+ // come first here too.
263
+ const {
264
+ pinned,
265
+ rest
266
+ } = splitPinned(visibleEvents, pinnedEvents, pinnedLiveIds);
267
+ return pinned.length === 0 ? rest : [...pinned, ...rest];
268
+ }, [fromSaved, savedRecords, savedSearch, isPro, visibleEvents, pinnedEvents, pinnedLiveIds]);
269
+
270
+ // Publish the navigable order. `setIds` re-anchors the cursor BY ID and
271
+ // no-ops when the ids are unchanged, so this is safe to run every render.
272
+ useEffect(() => {
273
+ networkSelectionStore.setIds(toSelectionIds(ordered));
274
+ }, [ordered]);
275
+ const {
276
+ index,
277
+ total
278
+ } = useNetworkCursor();
279
+
280
+ // Not in the list at all (filter changed under it, aged past the cap, or a
281
+ // saved snapshot with no live twin) — no footer rather than a counter that
282
+ // lies.
283
+ if (index < 0) return null;
284
+
285
+ // Both lists are newest-first, so "Previous" walks toward the NEWER request.
286
+ return /*#__PURE__*/_jsx(EventStepperFooter, {
287
+ currentIndex: index,
288
+ totalItems: total,
289
+ itemLabel: "Request",
290
+ applySafeAreaInset: applySafeAreaInset,
291
+ onPrevious: stepPrevious,
292
+ onNext: stepNext
293
+ });
294
+ });