@buoy-gg/network 7.0.32 → 7.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/network/components/NetworkCopySettingsView.js +10 -121
- package/lib/commonjs/network/components/NetworkHeaderLive.js +16 -0
- package/lib/commonjs/network/components/NetworkListScreen.js +34 -46
- package/lib/commonjs/network/components/NetworkModal.js +24 -53
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +11 -2
- package/lib/commonjs/network/components/NetworkSavedScreen.js +19 -38
- package/lib/commonjs/network/hooks/useNetworkEventList.js +7 -4
- package/lib/commonjs/network/sync/networkSyncAdapter.js +8 -0
- package/lib/commonjs/preset.js +6 -21
- package/lib/module/index.js +1 -1
- package/lib/module/network/components/NetworkCopySettingsView.js +13 -124
- package/lib/module/network/components/NetworkHeaderLive.js +15 -0
- package/lib/module/network/components/NetworkListScreen.js +35 -47
- package/lib/module/network/components/NetworkModal.js +26 -55
- package/lib/module/network/components/NetworkOverridesScreen.js +12 -3
- package/lib/module/network/components/NetworkSavedScreen.js +20 -39
- package/lib/module/network/hooks/useNetworkEventList.js +7 -4
- package/lib/module/network/sync/networkSyncAdapter.js +8 -0
- package/lib/module/preset.js +6 -20
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts +11 -0
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkSavedScreen.d.ts.map +1 -1
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts +11 -0
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +1 -1
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +4 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/preset.d.ts +2 -8
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { DynamicFilterView, macOSColors, FileText, FileCode, Link, Zap, Settings, Hash, Eye,
|
|
3
|
+
import { DynamicFilterView, macOSColors, FileText, FileCode, Link, Zap, Settings, Hash, Eye, ToolbarCopyButton, formatRelativeTime, useFeatureGate, ProFeatureBanner, ExportPreview } from "@buoy-gg/shared-ui";
|
|
4
4
|
import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
|
|
5
5
|
import { useCallback, useMemo, useState } from "react";
|
|
6
|
-
import { Text, StyleSheet, ScrollView, View
|
|
6
|
+
import { Text, StyleSheet, ScrollView, View } from "react-native";
|
|
7
7
|
import { useTickEveryMinute } from "../hooks/useTickEveryMinute";
|
|
8
|
-
import { jsx as _jsx,
|
|
8
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
9
|
export const DEFAULT_COPY_SETTINGS = {
|
|
10
10
|
includeMethod: true,
|
|
11
11
|
includeStatus: true,
|
|
@@ -541,62 +541,16 @@ export function NetworkCopySettingsView({
|
|
|
541
541
|
const generatePreviewContent = useCallback(() => {
|
|
542
542
|
const dataSource = hasLiveData ? events : [mockRequestData];
|
|
543
543
|
const requestCount = dataSource.length;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
style: styles.expandButtonContent,
|
|
555
|
-
children: [/*#__PURE__*/_jsx(Eye, {
|
|
556
|
-
size: 16,
|
|
557
|
-
color: macOSColors.semantic.info
|
|
558
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
559
|
-
style: styles.expandButtonTextContainer,
|
|
560
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
561
|
-
style: styles.expandButtonTitle,
|
|
562
|
-
children: "Show Preview"
|
|
563
|
-
}), /*#__PURE__*/_jsxs(Text, {
|
|
564
|
-
style: styles.expandButtonSubtitle,
|
|
565
|
-
children: [requestCount, " request", requestCount !== 1 ? 's' : '', " \u2022 ~", formatBytes(estimatedSize)]
|
|
566
|
-
})]
|
|
567
|
-
}), /*#__PURE__*/_jsx(ChevronDown, {
|
|
568
|
-
size: 16,
|
|
569
|
-
color: macOSColors.text.muted
|
|
570
|
-
})]
|
|
571
|
-
})
|
|
572
|
-
}), sizeWarningLevel !== 'none' && /*#__PURE__*/_jsxs(View, {
|
|
573
|
-
style: [styles.sizeWarning, sizeWarningLevel === 'danger' && styles.sizeWarningDanger],
|
|
574
|
-
children: [/*#__PURE__*/_jsx(AlertTriangle, {
|
|
575
|
-
size: 12,
|
|
576
|
-
color: warningColor
|
|
577
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
578
|
-
style: [styles.sizeWarningText, {
|
|
579
|
-
color: warningColor
|
|
580
|
-
}],
|
|
581
|
-
children: sizeWarningLevel === 'danger' ? 'Very large payload - may cause performance issues' : 'Large payload - preview may be slow'
|
|
582
|
-
})]
|
|
583
|
-
})]
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Expanded state - show collapse button + actual content
|
|
588
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
589
|
-
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
590
|
-
style: styles.collapseButton,
|
|
591
|
-
onPress: () => setIsPreviewExpanded(false),
|
|
592
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
593
|
-
style: styles.collapseButtonText,
|
|
594
|
-
children: "Hide Preview"
|
|
595
|
-
}), /*#__PURE__*/_jsx(ChevronUp, {
|
|
596
|
-
size: 14,
|
|
597
|
-
color: macOSColors.text.secondary
|
|
598
|
-
})]
|
|
599
|
-
}), generateExpandedPreviewContent()]
|
|
544
|
+
return /*#__PURE__*/_jsx(ExportPreview, {
|
|
545
|
+
expanded: isPreviewExpanded,
|
|
546
|
+
onExpandedChange: setIsPreviewExpanded,
|
|
547
|
+
summary: `${requestCount} request${requestCount !== 1 ? "s" : ""} • ~${formatBytes(estimatedSize)}`,
|
|
548
|
+
warningLevel: sizeWarningLevel,
|
|
549
|
+
warningText: {
|
|
550
|
+
warning: "Large payload - preview may be slow",
|
|
551
|
+
danger: "Very large payload - may cause performance issues"
|
|
552
|
+
},
|
|
553
|
+
renderBody: generateExpandedPreviewContent
|
|
600
554
|
});
|
|
601
555
|
}, [settings, hasLiveData, events, mockRequestData, isPreviewExpanded, estimatedSize, sizeWarningLevel, generateExpandedPreviewContent]);
|
|
602
556
|
|
|
@@ -785,71 +739,6 @@ const styles = StyleSheet.create({
|
|
|
785
739
|
marginRight: 8
|
|
786
740
|
},
|
|
787
741
|
// Collapsible preview styles
|
|
788
|
-
collapsedPreview: {
|
|
789
|
-
gap: 8
|
|
790
|
-
},
|
|
791
|
-
expandButton: {
|
|
792
|
-
backgroundColor: macOSColors.background.input,
|
|
793
|
-
borderRadius: 8,
|
|
794
|
-
borderWidth: 1,
|
|
795
|
-
borderColor: macOSColors.border.default,
|
|
796
|
-
padding: 12
|
|
797
|
-
},
|
|
798
|
-
expandButtonContent: {
|
|
799
|
-
flexDirection: 'row',
|
|
800
|
-
alignItems: 'center',
|
|
801
|
-
gap: 10
|
|
802
|
-
},
|
|
803
|
-
expandButtonTextContainer: {
|
|
804
|
-
flex: 1
|
|
805
|
-
},
|
|
806
|
-
expandButtonTitle: {
|
|
807
|
-
fontSize: 13,
|
|
808
|
-
fontWeight: '600',
|
|
809
|
-
color: macOSColors.text.primary
|
|
810
|
-
},
|
|
811
|
-
expandButtonSubtitle: {
|
|
812
|
-
fontSize: 11,
|
|
813
|
-
color: macOSColors.text.secondary,
|
|
814
|
-
marginTop: 2
|
|
815
|
-
},
|
|
816
|
-
collapseButton: {
|
|
817
|
-
flexDirection: 'row',
|
|
818
|
-
alignItems: 'center',
|
|
819
|
-
justifyContent: 'center',
|
|
820
|
-
gap: 6,
|
|
821
|
-
paddingVertical: 8,
|
|
822
|
-
marginBottom: 8,
|
|
823
|
-
backgroundColor: macOSColors.background.hover,
|
|
824
|
-
borderRadius: 6,
|
|
825
|
-
borderWidth: 1,
|
|
826
|
-
borderColor: macOSColors.border.default
|
|
827
|
-
},
|
|
828
|
-
collapseButtonText: {
|
|
829
|
-
fontSize: 12,
|
|
830
|
-
fontWeight: '500',
|
|
831
|
-
color: macOSColors.text.secondary
|
|
832
|
-
},
|
|
833
|
-
sizeWarning: {
|
|
834
|
-
flexDirection: 'row',
|
|
835
|
-
alignItems: 'center',
|
|
836
|
-
gap: 6,
|
|
837
|
-
paddingHorizontal: 10,
|
|
838
|
-
paddingVertical: 6,
|
|
839
|
-
backgroundColor: macOSColors.semantic.warningBackground,
|
|
840
|
-
borderRadius: 6,
|
|
841
|
-
borderWidth: 1,
|
|
842
|
-
borderColor: macOSColors.semantic.warning + '30'
|
|
843
|
-
},
|
|
844
|
-
sizeWarningDanger: {
|
|
845
|
-
backgroundColor: macOSColors.semantic.errorBackground,
|
|
846
|
-
borderColor: macOSColors.semantic.error + '30'
|
|
847
|
-
},
|
|
848
|
-
sizeWarningText: {
|
|
849
|
-
fontSize: 11,
|
|
850
|
-
fontWeight: '500',
|
|
851
|
-
flex: 1
|
|
852
|
-
},
|
|
853
742
|
// Gated container styles for free users
|
|
854
743
|
gatedContainer: {
|
|
855
744
|
flex: 1,
|
|
@@ -40,6 +40,21 @@ export function NetworkHeaderBadges({
|
|
|
40
40
|
ignoredPatterns,
|
|
41
41
|
paused
|
|
42
42
|
});
|
|
43
|
+
return /*#__PURE__*/_jsx(NetworkHeaderBadgesView, {
|
|
44
|
+
counts: counts,
|
|
45
|
+
activeStatus: activeStatus,
|
|
46
|
+
onToggleStatus: onToggleStatus
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The chips themselves, fed by props — what the parity sheet renders with
|
|
51
|
+
* fixture counts (`NetworkHeaderBadges` reads the live store).
|
|
52
|
+
*/
|
|
53
|
+
export function NetworkHeaderBadgesView({
|
|
54
|
+
counts,
|
|
55
|
+
activeStatus,
|
|
56
|
+
onToggleStatus
|
|
57
|
+
}) {
|
|
43
58
|
return /*#__PURE__*/_jsxs(View, {
|
|
44
59
|
style: styles.headerChipRow,
|
|
45
60
|
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { useCallback } from "react";
|
|
16
16
|
import { FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
17
|
-
import { FlaskConical,
|
|
17
|
+
import { FlaskConical, Pin, Power, macOSColors, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET } from "@buoy-gg/shared-ui";
|
|
18
18
|
import { NetworkEventItemCompact } from "./NetworkEventItemCompact";
|
|
19
19
|
import { NetworkUpgradeBanner } from "./NetworkUpgradeBanner";
|
|
20
20
|
import { useNetworkEventList } from "../hooks/useNetworkEventList";
|
|
@@ -23,23 +23,6 @@ import { useOverrideRules } from "../hooks/useOverrideRules";
|
|
|
23
23
|
import { ruleMatches } from "../overrides/matchRule";
|
|
24
24
|
import { splitPinned } from "../utils/withPinnedFirst";
|
|
25
25
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
26
|
-
function EmptyState({
|
|
27
|
-
isEnabled
|
|
28
|
-
}) {
|
|
29
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
30
|
-
style: styles.emptyState,
|
|
31
|
-
children: [/*#__PURE__*/_jsx(Globe, {
|
|
32
|
-
size: 32,
|
|
33
|
-
color: macOSColors.text.muted
|
|
34
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
35
|
-
style: styles.emptyTitle,
|
|
36
|
-
children: "No network events"
|
|
37
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
38
|
-
style: styles.emptyText,
|
|
39
|
-
children: isEnabled ? "Network requests will appear here" : "Enable interception to start capturing"
|
|
40
|
-
})]
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
26
|
const keyExtractor = item => item.id;
|
|
44
27
|
|
|
45
28
|
/**
|
|
@@ -160,7 +143,8 @@ export function NetworkListScreen({
|
|
|
160
143
|
const {
|
|
161
144
|
visibleEvents,
|
|
162
145
|
hasLockedEvents,
|
|
163
|
-
lockedEventCount
|
|
146
|
+
lockedEventCount,
|
|
147
|
+
totalEvents
|
|
164
148
|
} = useNetworkEventList({
|
|
165
149
|
filter,
|
|
166
150
|
ignoredPatterns,
|
|
@@ -222,6 +206,34 @@ export function NetworkListScreen({
|
|
|
222
206
|
onLongPress: handleLongPress,
|
|
223
207
|
saved: savedLiveIds.has(item.id)
|
|
224
208
|
});
|
|
209
|
+
|
|
210
|
+
// Which flavour of "nothing here" this is. Pins and overrides render
|
|
211
|
+
// outside the list, so a screen showing only those is not empty at all.
|
|
212
|
+
const renderEmpty = () => {
|
|
213
|
+
// Pins and overrides render outside the list, so a screen showing only
|
|
214
|
+
// those is not empty at all.
|
|
215
|
+
if (pinnedRows.length > 0 || overridden.length > 0) return null;
|
|
216
|
+
return /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
217
|
+
testID: "buoy-network-empty",
|
|
218
|
+
...resolveListEmptyState({
|
|
219
|
+
subject: "requests",
|
|
220
|
+
search: searchText,
|
|
221
|
+
total: totalEvents,
|
|
222
|
+
capturing: isEnabled,
|
|
223
|
+
waiting: {
|
|
224
|
+
// fishing = capture is on and nothing has arrived.
|
|
225
|
+
otto: "fishing",
|
|
226
|
+
title: "No network events",
|
|
227
|
+
message: "Network requests will appear here"
|
|
228
|
+
},
|
|
229
|
+
paused: {
|
|
230
|
+
// paused = capture off. Both are literally what the copy says.
|
|
231
|
+
title: "No network events",
|
|
232
|
+
message: "Enable interception to start capturing"
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
};
|
|
225
237
|
const renderListFooter = () => {
|
|
226
238
|
if (!hasLockedEvents) return null;
|
|
227
239
|
const isSearching = searchText.length > 0;
|
|
@@ -266,10 +278,8 @@ export function NetworkListScreen({
|
|
|
266
278
|
showsVerticalScrollIndicator: true
|
|
267
279
|
// Clipped cells falsely fail Detox's hittability check (known
|
|
268
280
|
// RN+Detox issue), so e2e bundles (Metro started with
|
|
269
|
-
// EXPO_PUBLIC_E2E=1) turn clipping off
|
|
270
|
-
// guarded so a non-inlining bundler keeps the prod behavior.
|
|
281
|
+
// EXPO_PUBLIC_E2E=1) turn clipping off — see EVENT_LIST_PRESET.
|
|
271
282
|
,
|
|
272
|
-
removeClippedSubviews: !(typeof process !== "undefined" && process.env?.EXPO_PUBLIC_E2E),
|
|
273
283
|
onEndReachedThreshold: 0.8
|
|
274
284
|
// The list OWNS scrolling (NetworkModal passes
|
|
275
285
|
// disableScrollWrapper while this screen is visible) so FlatList
|
|
@@ -278,24 +288,18 @@ export function NetworkListScreen({
|
|
|
278
288
|
// rows: at the store's 500-event cap that measured 17 JS FPS /
|
|
279
289
|
// 133% CPU / 1.4GB vs ~53 JS FPS / 52% / 360MB virtualized
|
|
280
290
|
// (buoy-optimize batch 2026-07-06, batch-1783396983529-6l7w).
|
|
281
|
-
// Window/batch values are the batch's "tuned" winner.
|
|
291
|
+
// Window/batch values are the batch's "tuned" winner (EVENT_LIST_PRESET).
|
|
282
292
|
,
|
|
283
293
|
scrollEnabled: true,
|
|
284
|
-
|
|
285
|
-
maxToRenderPerBatch: 8,
|
|
286
|
-
windowSize: 5,
|
|
287
|
-
updateCellsBatchingPeriod: 50
|
|
294
|
+
...EVENT_LIST_PRESET,
|
|
288
295
|
// Saving a request from the detail view changes a row's glyph
|
|
289
296
|
// without changing `data`. VirtualizedList is a PureComponent, so
|
|
290
297
|
// the Set has to be declared as a re-render marker rather than
|
|
291
298
|
// relying on `renderItem` getting a fresh identity.
|
|
292
|
-
,
|
|
293
299
|
extraData: savedLiveIds,
|
|
294
300
|
"sentry-label": "ignore network events list",
|
|
295
301
|
ListFooterComponent: renderListFooter
|
|
296
|
-
}) :
|
|
297
|
-
isEnabled: isEnabled
|
|
298
|
-
})]
|
|
302
|
+
}) : renderEmpty()]
|
|
299
303
|
});
|
|
300
304
|
}
|
|
301
305
|
const styles = StyleSheet.create({
|
|
@@ -392,21 +396,5 @@ const styles = StyleSheet.create({
|
|
|
392
396
|
},
|
|
393
397
|
listContent: {
|
|
394
398
|
paddingTop: 8
|
|
395
|
-
},
|
|
396
|
-
emptyState: {
|
|
397
|
-
alignItems: "center",
|
|
398
|
-
paddingVertical: 40
|
|
399
|
-
},
|
|
400
|
-
emptyTitle: {
|
|
401
|
-
color: macOSColors.text.primary,
|
|
402
|
-
fontSize: 14,
|
|
403
|
-
fontWeight: "600",
|
|
404
|
-
marginTop: 12,
|
|
405
|
-
marginBottom: 6
|
|
406
|
-
},
|
|
407
|
-
emptyText: {
|
|
408
|
-
color: macOSColors.text.muted,
|
|
409
|
-
fontSize: 12,
|
|
410
|
-
textAlign: "center"
|
|
411
399
|
}
|
|
412
400
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useEffect, useCallback } from "react";
|
|
4
|
-
import { View, StyleSheet, TouchableOpacity
|
|
5
|
-
import { Search, Filter, Bookmark,
|
|
4
|
+
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
5
|
+
import { Search, Filter, Bookmark, JsModal, ModalHeader, devToolsStorageKeys, macOSColors, night, persistentStorage, NightSegmented, useFeatureGate, UpgradeModal, PowerToggleButton, useIgnoredPatterns, windowDragProps } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { NetworkFilterViewV3 } from "./NetworkFilterViewV3";
|
|
7
7
|
import { TickProvider } from "../hooks/useTickEveryMinute";
|
|
8
8
|
import { NetworkEventDetailView } from "./NetworkEventDetailView";
|
|
@@ -230,6 +230,12 @@ function NetworkModalInner({
|
|
|
230
230
|
const [activeTab, setActiveTab] = useState("filters");
|
|
231
231
|
const [searchText, setSearchText] = useState("");
|
|
232
232
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
233
|
+
// True while the header's clear ✕ is being pressed. With a mouse, pressing
|
|
234
|
+
// the ✕ blurs the input FIRST, and the blur handler below closes search —
|
|
235
|
+
// which unmounts the ✕ before its own press can fire, so the query was
|
|
236
|
+
// never cleared (desktop). Press-in lands before the blur, so the blur
|
|
237
|
+
// handler can tell this dismissal apart from a click elsewhere.
|
|
238
|
+
const clearPressRef = useRef(false);
|
|
233
239
|
// The Saved screen's search box lives HERE, not in that screen, because the
|
|
234
240
|
// detail footer steps through the same list the user was just looking at —
|
|
235
241
|
// and the Saved screen is unmounted while a detail is open.
|
|
@@ -441,38 +447,24 @@ function NetworkModalInner({
|
|
|
441
447
|
onBack: onBack
|
|
442
448
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
443
449
|
title: "",
|
|
444
|
-
children: isSearchActive ? /*#__PURE__*/
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
autoCorrect: false,
|
|
463
|
-
returnKeyType: "search"
|
|
464
|
-
}), searchText.length > 0 ? /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
465
|
-
onPress: () => {
|
|
466
|
-
handleSearch("");
|
|
467
|
-
setIsSearchActive(false);
|
|
468
|
-
},
|
|
469
|
-
"sentry-label": "ignore clear search header",
|
|
470
|
-
style: styles.headerSearchClear,
|
|
471
|
-
children: /*#__PURE__*/_jsx(X, {
|
|
472
|
-
size: 14,
|
|
473
|
-
color: night.color.textSecondary
|
|
474
|
-
})
|
|
475
|
-
}) : null]
|
|
450
|
+
children: isSearchActive ? /*#__PURE__*/_jsx(ModalHeader.Search, {
|
|
451
|
+
value: searchText,
|
|
452
|
+
onChangeText: handleSearch,
|
|
453
|
+
onCancel: () => {
|
|
454
|
+
handleSearch("");
|
|
455
|
+
setIsSearchActive(false);
|
|
456
|
+
},
|
|
457
|
+
onDismiss: () => setIsSearchActive(false),
|
|
458
|
+
dismissOnBlur: true,
|
|
459
|
+
clearOnlyWhenText: true,
|
|
460
|
+
placeholder: "Search URL, method, operation, error...",
|
|
461
|
+
accessibilityLabel: "Search network requests",
|
|
462
|
+
iconSize: 14,
|
|
463
|
+
testID: "buoy-network-search-input",
|
|
464
|
+
clearTestID: "buoy-network-search-clear",
|
|
465
|
+
inputProps: {
|
|
466
|
+
"sentry-label": "ignore network search header"
|
|
467
|
+
}
|
|
476
468
|
}) : /*#__PURE__*/_jsx(NetworkHeaderBadges, {
|
|
477
469
|
activeStatus: filter.status,
|
|
478
470
|
onToggleStatus: toggleStatusFilter,
|
|
@@ -717,27 +709,6 @@ const styles = StyleSheet.create({
|
|
|
717
709
|
},
|
|
718
710
|
// Night chrome — the header is glass over the star field, so the search
|
|
719
711
|
// field and action chips sit on the Everlights dark-glass fill.
|
|
720
|
-
headerSearchContainer: {
|
|
721
|
-
flexDirection: "row",
|
|
722
|
-
alignItems: "center",
|
|
723
|
-
backgroundColor: night.color.surfaceElevated,
|
|
724
|
-
borderRadius: night.radius.row,
|
|
725
|
-
borderWidth: 1,
|
|
726
|
-
borderColor: night.color.border,
|
|
727
|
-
paddingHorizontal: 12,
|
|
728
|
-
paddingVertical: 5
|
|
729
|
-
},
|
|
730
|
-
headerSearchInput: {
|
|
731
|
-
flex: 1,
|
|
732
|
-
color: night.color.text,
|
|
733
|
-
fontSize: 13,
|
|
734
|
-
marginLeft: 6,
|
|
735
|
-
paddingVertical: 2
|
|
736
|
-
},
|
|
737
|
-
headerSearchClear: {
|
|
738
|
-
marginLeft: 6,
|
|
739
|
-
padding: 4
|
|
740
|
-
},
|
|
741
712
|
menuCopyRow: {
|
|
742
713
|
paddingHorizontal: 6
|
|
743
714
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useState } from "react";
|
|
4
4
|
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
5
|
-
import { AlertCircle, AlertTriangle, DevToolsCard, EmptyState,
|
|
5
|
+
import { AlertCircle, AlertTriangle, DevToolsCard, EmptyState, MethodBadge, Plus, Trash2, macOSColors, useLimit } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { useOverrideRules } from "../hooks/useOverrideRules";
|
|
7
7
|
import { NetworkOverrideEditor } from "./NetworkOverrideEditor";
|
|
8
8
|
import { overrideRulesStore } from "../overrides/overrideRulesStore";
|
|
@@ -361,8 +361,17 @@ function OverridesBody({
|
|
|
361
361
|
})]
|
|
362
362
|
}), !hasRules ? /*#__PURE__*/_jsx(View, {
|
|
363
363
|
style: styles.emptyWrap,
|
|
364
|
-
children: /*#__PURE__*/_jsx(EmptyState
|
|
365
|
-
|
|
364
|
+
children: /*#__PURE__*/_jsx(EmptyState
|
|
365
|
+
// Otto instead of the flask: nothing is faked yet, so he peers
|
|
366
|
+
// into an empty box. Truthful by construction — this branch only
|
|
367
|
+
// renders when the rule list is genuinely empty, and the master
|
|
368
|
+
// switch bar above is hidden in exactly that case.
|
|
369
|
+
, {
|
|
370
|
+
otto: "empty"
|
|
371
|
+
// 32 → pixelSize 2, the size the saved/list screens use, so the
|
|
372
|
+
// three network empties read as one family.
|
|
373
|
+
,
|
|
374
|
+
iconSize: 32,
|
|
366
375
|
title: "Nothing overridden"
|
|
367
376
|
// Names the control that actually exists. This said "tap Force 500"
|
|
368
377
|
// — a button from the deleted preset list, which sent anyone
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import { useState } from "react";
|
|
21
21
|
import { FlatList, StyleSheet, Text, TextInput, View, TouchableOpacity } from "react-native";
|
|
22
|
-
import { Bookmark, CopyButton, Search, macOSColors, useLimit } from "@buoy-gg/shared-ui";
|
|
22
|
+
import { Bookmark, CopyButton, Search, macOSColors, useLimit, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET } from "@buoy-gg/shared-ui";
|
|
23
23
|
import { NetworkEventItemCompact } from "./NetworkEventItemCompact";
|
|
24
24
|
import { NetworkUpgradeBanner } from "./NetworkUpgradeBanner";
|
|
25
25
|
import { generateNetworkCopyText } from "../utils/generateNetworkCopyText";
|
|
@@ -28,21 +28,6 @@ import { useNetworkSaved } from "../hooks/useNetworkSaved";
|
|
|
28
28
|
import { MAX_SAVED } from "../utils/networkSavedStore";
|
|
29
29
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
30
30
|
const keyExtractor = item => item.id;
|
|
31
|
-
function EmptyState() {
|
|
32
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
33
|
-
style: styles.emptyState,
|
|
34
|
-
children: [/*#__PURE__*/_jsx(Bookmark, {
|
|
35
|
-
size: 32,
|
|
36
|
-
color: macOSColors.text.muted
|
|
37
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
38
|
-
style: styles.emptyTitle,
|
|
39
|
-
children: "No saved requests"
|
|
40
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
41
|
-
style: styles.emptyText,
|
|
42
|
-
children: "Open a request and tap the bookmark to keep it here \u2014 saved requests survive Clear and app restarts."
|
|
43
|
-
})]
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
31
|
export function NetworkSavedScreen({
|
|
47
32
|
visible,
|
|
48
33
|
isPro,
|
|
@@ -142,17 +127,31 @@ export function NetworkSavedScreen({
|
|
|
142
127
|
autoCorrect: false,
|
|
143
128
|
returnKeyType: "search"
|
|
144
129
|
})]
|
|
145
|
-
}) : null, savedRecords.length === 0
|
|
130
|
+
}) : null, savedRecords.length === 0 ||
|
|
131
|
+
// lockedCount > 0 falls through to the list so the upgrade banner (its
|
|
132
|
+
// ListFooterComponent) still renders — "0 shown, N locked" is an
|
|
133
|
+
// answer, not an empty screen.
|
|
134
|
+
search.trim().length > 0 && filtered.length === 0 && lockedCount === 0 ? /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
135
|
+
testID: "buoy-network-saved-empty",
|
|
136
|
+
...resolveListEmptyState({
|
|
137
|
+
subject: "saved requests",
|
|
138
|
+
verb: "kept",
|
|
139
|
+
search: savedRecords.length === 0 ? "" : search,
|
|
140
|
+
total: savedRecords.length,
|
|
141
|
+
waiting: {
|
|
142
|
+
otto: "empty",
|
|
143
|
+
title: "No saved requests",
|
|
144
|
+
message: "Open a request and tap the bookmark to keep it here — saved requests survive Clear and app restarts."
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
}) : /*#__PURE__*/_jsx(FlatList, {
|
|
146
148
|
data: filtered,
|
|
147
149
|
renderItem: renderItem,
|
|
148
150
|
keyExtractor: keyExtractor,
|
|
149
151
|
contentContainerStyle: styles.listContent,
|
|
150
152
|
showsVerticalScrollIndicator: true,
|
|
151
|
-
removeClippedSubviews: true,
|
|
152
153
|
scrollEnabled: true,
|
|
153
|
-
|
|
154
|
-
maxToRenderPerBatch: 8,
|
|
155
|
-
windowSize: 5,
|
|
154
|
+
...EVENT_LIST_PRESET,
|
|
156
155
|
extraData: pinnedLiveIds,
|
|
157
156
|
"sentry-label": "ignore saved requests list",
|
|
158
157
|
ListFooterComponent: renderListFooter
|
|
@@ -219,23 +218,5 @@ const styles = StyleSheet.create({
|
|
|
219
218
|
},
|
|
220
219
|
listContent: {
|
|
221
220
|
paddingTop: 4
|
|
222
|
-
},
|
|
223
|
-
emptyState: {
|
|
224
|
-
alignItems: "center",
|
|
225
|
-
paddingVertical: 40,
|
|
226
|
-
paddingHorizontal: 24
|
|
227
|
-
},
|
|
228
|
-
emptyTitle: {
|
|
229
|
-
color: macOSColors.text.primary,
|
|
230
|
-
fontSize: 14,
|
|
231
|
-
fontWeight: "600",
|
|
232
|
-
marginTop: 12,
|
|
233
|
-
marginBottom: 6
|
|
234
|
-
},
|
|
235
|
-
emptyText: {
|
|
236
|
-
color: macOSColors.text.muted,
|
|
237
|
-
fontSize: 12,
|
|
238
|
-
textAlign: "center",
|
|
239
|
-
lineHeight: 17
|
|
240
221
|
}
|
|
241
222
|
});
|
|
@@ -74,9 +74,11 @@ export function useNetworkEventList(options) {
|
|
|
74
74
|
|
|
75
75
|
// Derivations are plain useMemo over the subscribed array — linear
|
|
76
76
|
// passes over ≤maxEvents items, recomputed only when inputs change.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
// Split in two so the pre-filter count is observable: `totalEvents` below
|
|
78
|
+
// is the denominator the empty state needs, and recomputing it separately
|
|
79
|
+
// would mean a second pass over the same array.
|
|
80
|
+
const candidateEvents = useMemo(() => applyIgnoredPatterns(events, ignoredPatterns), [events, ignoredPatterns]);
|
|
81
|
+
const filteredEvents = useMemo(() => filterNetworkEvents(candidateEvents, filter), [candidateEvents, filter]);
|
|
80
82
|
|
|
81
83
|
// Free tier: events beyond the limit in the ORIGINAL order are locked,
|
|
82
84
|
// so locked events stay locked even when searching (ported verbatim).
|
|
@@ -96,6 +98,7 @@ export function useNetworkEventList(options) {
|
|
|
96
98
|
events: filteredEvents,
|
|
97
99
|
visibleEvents,
|
|
98
100
|
hasLockedEvents: lockedEventCount > 0,
|
|
99
|
-
lockedEventCount
|
|
101
|
+
lockedEventCount,
|
|
102
|
+
totalEvents: candidateEvents.length
|
|
100
103
|
};
|
|
101
104
|
}
|
|
@@ -428,6 +428,14 @@ export const networkSyncAdapter = {
|
|
|
428
428
|
const event = resolveEvent(id);
|
|
429
429
|
if (!event) return null;
|
|
430
430
|
return {
|
|
431
|
+
// Identity + the override mark ride along so a caller (Ask Buoy's
|
|
432
|
+
// receipt, an MCP session) can say WHOSE body this is and whether an
|
|
433
|
+
// override rule produced it — a mocked response rendered as "live"
|
|
434
|
+
// is the fabrication problem wearing a chrome suit.
|
|
435
|
+
url: event.url ?? null,
|
|
436
|
+
method: event.method ?? null,
|
|
437
|
+
status: event.status ?? null,
|
|
438
|
+
override: event.override ?? null,
|
|
431
439
|
requestData: event.requestData ?? null,
|
|
432
440
|
responseData: event.responseData ?? null,
|
|
433
441
|
requestHeaders: event.requestHeaders ?? null,
|
package/lib/module/preset.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { NetworkIcon } from "@buoy-gg/shared-ui";
|
|
20
|
+
import { NetworkIcon, createToolFactory, defineToolPreset } from "@buoy-gg/shared-ui";
|
|
21
21
|
import { NetworkModal } from "./network/components/NetworkModal";
|
|
22
22
|
import { overrideRulesStore } from "./network/overrides/overrideRulesStore";
|
|
23
23
|
import { networkEventStore } from "./network/utils/networkEventStore";
|
|
@@ -63,7 +63,7 @@ if (isReactNativeRuntime && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
|
63
63
|
* - Filter by status, method, URL
|
|
64
64
|
* - Timing information
|
|
65
65
|
*/
|
|
66
|
-
export const networkToolPreset = {
|
|
66
|
+
export const networkToolPreset = defineToolPreset({
|
|
67
67
|
id: "network",
|
|
68
68
|
name: "NET",
|
|
69
69
|
description: "Network request logger",
|
|
@@ -77,7 +77,7 @@ export const networkToolPreset = {
|
|
|
77
77
|
props: {
|
|
78
78
|
enableSharedModalDimensions: false
|
|
79
79
|
}
|
|
80
|
-
};
|
|
80
|
+
});
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Create a custom network monitoring tool configuration.
|
|
@@ -94,20 +94,6 @@ export const networkToolPreset = {
|
|
|
94
94
|
* });
|
|
95
95
|
* ```
|
|
96
96
|
*/
|
|
97
|
-
export
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
name: options?.name || "NET",
|
|
101
|
-
description: options?.description || "Network request logger",
|
|
102
|
-
slot: "both",
|
|
103
|
-
icon: ({
|
|
104
|
-
size
|
|
105
|
-
}) => /*#__PURE__*/_jsx(NetworkIcon, {
|
|
106
|
-
size: size
|
|
107
|
-
}),
|
|
108
|
-
component: NetworkModal,
|
|
109
|
-
props: {
|
|
110
|
-
enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}
|
|
97
|
+
export const createNetworkTool = createToolFactory(networkToolPreset, options => ({
|
|
98
|
+
enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
|
|
99
|
+
}));
|
|
@@ -13,7 +13,7 @@ export type { NetworkToolViewProps } from "./network/components/NetworkModal";
|
|
|
13
13
|
export { NetworkEventDetailView } from "./network/components/NetworkEventDetailView";
|
|
14
14
|
export { NetworkEventItemCompact } from "./network/components/NetworkEventItemCompact";
|
|
15
15
|
export { NetworkSavedScreen } from "./network/components/NetworkSavedScreen";
|
|
16
|
-
export { NetworkSavedButton, NetworkDetailPinActions, } from "./network/components/NetworkHeaderLive";
|
|
16
|
+
export { NetworkSavedButton, NetworkDetailPinActions, NetworkHeaderBadgesView, } from "./network/components/NetworkHeaderLive";
|
|
17
17
|
export { useNetworkEvents } from "./network/hooks/useNetworkEvents";
|
|
18
18
|
export { useNetworkEventList } from "./network/hooks/useNetworkEventList";
|
|
19
19
|
export { useLiveNetworkEvent } from "./network/hooks/useLiveNetworkEvent";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAIhF,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,qBAAqB,GAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAKtF,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAKpC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB;AACtB;;;GAGG;AACH,KAAK,mBAAmB,GACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,SAAS,IAAI,kBAAkB,EAC/B,oBAAoB,IAAI,wBAAwB,EAChD,UAAU,IAAI,mBAAmB,EACjC,UAAU,IAAI,mBAAmB,EACjC,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AAQnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAO5C,gBAAgB;AAChB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE;;;GAGG;AACH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAIhF,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,qBAAqB,GAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAKtF,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAKpC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB;AACtB;;;GAGG;AACH,KAAK,mBAAmB,GACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,SAAS,IAAI,kBAAkB,EAC/B,oBAAoB,IAAI,wBAAwB,EAChD,UAAU,IAAI,mBAAmB,EACjC,UAAU,IAAI,mBAAmB,EACjC,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AAQnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAO5C,gBAAgB;AAChB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE;;;GAGG;AACH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC"}
|