@buoy-gg/network 7.0.34 → 7.0.36

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 (38) hide show
  1. package/lib/commonjs/network/components/NetworkCopySettingsView.js +2 -2
  2. package/lib/commonjs/network/components/NetworkEventDetailView.js +6 -6
  3. package/lib/commonjs/network/components/NetworkEventItemCompact.js +3 -3
  4. package/lib/commonjs/network/components/NetworkFilterViewV3.js +3 -3
  5. package/lib/commonjs/network/components/NetworkHeaderLive.js +2 -2
  6. package/lib/commonjs/network/components/NetworkListScreen.js +1 -1
  7. package/lib/commonjs/network/components/NetworkOverrideEditor.js +1 -1
  8. package/lib/commonjs/network/components/NetworkOverridesScreen.js +1 -1
  9. package/lib/commonjs/network/components/NetworkSavedScreen.js +1 -1
  10. package/lib/commonjs/network/components/response-editor/Explorer.js +13 -13
  11. package/lib/commonjs/network/sync/networkSyncAdapter.js +198 -2
  12. package/lib/commonjs/network/utils/networkListener.js +156 -2
  13. package/lib/module/network/components/NetworkCopySettingsView.js +3 -3
  14. package/lib/module/network/components/NetworkEventDetailView.js +7 -7
  15. package/lib/module/network/components/NetworkEventItemCompact.js +4 -4
  16. package/lib/module/network/components/NetworkFilterViewV3.js +4 -4
  17. package/lib/module/network/components/NetworkHeaderLive.js +3 -3
  18. package/lib/module/network/components/NetworkListScreen.js +2 -2
  19. package/lib/module/network/components/NetworkOverrideEditor.js +2 -2
  20. package/lib/module/network/components/NetworkOverridesScreen.js +2 -2
  21. package/lib/module/network/components/NetworkSavedScreen.js +2 -2
  22. package/lib/module/network/components/response-editor/Explorer.js +14 -14
  23. package/lib/module/network/sync/networkSyncAdapter.js +199 -2
  24. package/lib/module/network/utils/networkListener.js +156 -2
  25. package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +1 -1
  26. package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
  27. package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -1
  28. package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +1 -1
  29. package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -1
  30. package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
  31. package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +1 -1
  32. package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -1
  33. package/lib/typescript/network/components/NetworkSavedScreen.d.ts.map +1 -1
  34. package/lib/typescript/network/sync/networkSyncAdapter.d.ts +12 -0
  35. package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
  36. package/lib/typescript/network/utils/networkListener.d.ts +11 -27
  37. package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
  38. package/package.json +2 -2
@@ -71,6 +71,46 @@ const BUOY_XHR_INTERCEPTOR_FLAG = exports.BUOY_XHR_INTERCEPTOR_FLAG = "__buoyNet
71
71
  * @performance Uses lazy singleton pattern to minimize memory footprint
72
72
  * @performance Includes URL filtering to ignore development traffic
73
73
  */
74
+ /**
75
+ * Hosts another Buoy package has claimed as its own traffic.
76
+ *
77
+ * A GLOBAL, not an import: `@buoy-gg/network` cannot depend on `agent-core`
78
+ * (Ask Buoy is optional and this package is older), and `agent-core` ships
79
+ * with no dependencies at all. `registerSelfEndpoint` in agent-core writes the
80
+ * set; this reads it. Absent, undefined or the wrong shape all mean "nothing
81
+ * to exclude", so nothing breaks in an app without Ask Buoy.
82
+ *
83
+ * WHY IT IS NOT ENOUGH TO FILTER LATER. The interceptor reads the response
84
+ * body — `await processResponseBody(response.clone())` — BEFORE handing the
85
+ * response back. On a streamed reply that resolves only at EOF, so an
86
+ * intercepted model endpoint delivers its whole answer at once and Ask Buoy
87
+ * cannot stream a word. Ignoring the URL is what keeps the socket the
88
+ * caller's. It also keeps override rules off model traffic, which a broad
89
+ * user-authored rule would otherwise rewrite.
90
+ */
91
+ /**
92
+ * Content types whose body arrives over time rather than all at once.
93
+ *
94
+ * These are the ones where reading the clone before returning the response
95
+ * changes the CALLER's behaviour instead of merely observing it.
96
+ */
97
+ const STREAMED_CONTENT_TYPES = ["text/event-stream", "application/x-ndjson", "application/stream+json"];
98
+ const STREAMING_PLACEHOLDER = "[streaming\u2026]";
99
+ function isStreamedContentType(contentType) {
100
+ if (!contentType) return false;
101
+ const lower = contentType.toLowerCase();
102
+ return STREAMED_CONTENT_TYPES.some(t => lower.includes(t));
103
+ }
104
+ const INTERNAL_HOSTS_KEY = "__BUOY_INTERNAL_HOSTS__";
105
+ function isInternalBuoyHost(url) {
106
+ const hosts = globalThis[INTERNAL_HOSTS_KEY];
107
+ if (!(hosts instanceof Set) || hosts.size === 0) return false;
108
+ const lower = url.toLowerCase();
109
+ for (const host of hosts) {
110
+ if (typeof host === "string" && host && lower.includes(host)) return true;
111
+ }
112
+ return false;
113
+ }
74
114
  class NetworkListener {
75
115
  listeners = [];
76
116
  isListening = false;
@@ -121,7 +161,8 @@ class NetworkListener {
121
161
  * @returns True if the URL should be ignored
122
162
  */
123
163
  shouldIgnoreUrl(url) {
124
- return this.ignoredUrls.some(pattern => pattern.test(url));
164
+ if (this.ignoredUrls.some(pattern => pattern.test(url))) return true;
165
+ return isInternalBuoyHost(url);
125
166
  }
126
167
 
127
168
  // Emit event to all listeners
@@ -167,6 +208,70 @@ class NetworkListener {
167
208
  * @param maxSize - Maximum body size in bytes (default: 1MB)
168
209
  * @returns Object containing body, size, and truncation status
169
210
  */
211
+ /**
212
+ * Read a streamed body in the background, with a hard ceiling.
213
+ *
214
+ * Deliberately not `processResponseBody`: that one calls `.text()`, which
215
+ * accumulates the WHOLE body before it checks the size, so a long-lived SSE
216
+ * stream would grow unbounded in memory with nothing to stop it. This stops
217
+ * at the cap and cancels the reader, and a runtime with no `body` (React
218
+ * Native's own fetch) falls back to `.text()` — that response was never
219
+ * streaming there in the first place.
220
+ */
221
+ async readStreamedBody(response, maxSize = 256 * 1024) {
222
+ const body = response.body;
223
+ if (!body || typeof body.getReader !== "function") {
224
+ const text = await response.text();
225
+ return text.length > maxSize ? {
226
+ body: `${text.slice(0, maxSize)}\n\n... [truncated]`,
227
+ size: text.length
228
+ } : {
229
+ body: text,
230
+ size: text.length
231
+ };
232
+ }
233
+ const reader = body.getReader();
234
+ const decoder = new TextDecoder();
235
+ let out = "";
236
+ let size = 0;
237
+ let capped = false;
238
+ try {
239
+ for (;;) {
240
+ const {
241
+ done,
242
+ value
243
+ } = await reader.read();
244
+ if (done) break;
245
+ size += value?.byteLength ?? 0;
246
+ if (!capped) {
247
+ out += decoder.decode(value, {
248
+ stream: true
249
+ });
250
+ if (out.length >= maxSize) {
251
+ out = `${out.slice(0, maxSize)}\n\n... [truncated]`;
252
+ capped = true;
253
+ break;
254
+ }
255
+ }
256
+ }
257
+ if (!capped) out += decoder.decode();
258
+ } finally {
259
+ try {
260
+ void Promise.resolve(reader.cancel()).catch(() => {});
261
+ } catch {
262
+ /* already gone */
263
+ }
264
+ try {
265
+ reader.releaseLock();
266
+ } catch {
267
+ /* already released */
268
+ }
269
+ }
270
+ return {
271
+ body: out,
272
+ size
273
+ };
274
+ }
170
275
  async processResponseBody(response, maxSize = 1024 * 1024 // 1MB default
171
276
  ) {
172
277
  try {
@@ -544,6 +649,8 @@ class NetworkListener {
544
649
  }
545
650
 
546
651
  // Parse request body
652
+ // Explicitly typed: the late emit for a streamed reply reads this from
653
+ // inside a callback, which defeats the inference it used to rely on.
547
654
  let requestData;
548
655
  if (init?.body) {
549
656
  if (typeof init.body === "string") {
@@ -612,12 +719,29 @@ class NetworkListener {
612
719
  // look faster through fetch than through XHR.
613
720
  const ttfb = Date.now() - startTime;
614
721
 
722
+ /**
723
+ * A STREAMED reply is never awaited here.
724
+ *
725
+ * `processResponseBody` reads the clone to the end, and for
726
+ * `text/event-stream` the end is when the server finishes — seconds
727
+ * later, for an LLM. Awaiting it made the interceptor hold the
728
+ * response back until then, so every consumer of a streaming endpoint
729
+ * inside an app with capture on received the whole thing at once and
730
+ * could not stream a word of it. The row is emitted now with what is
731
+ * known, and the body is filled in when the stream closes: the store
732
+ * merges a second `response` event onto the same request id.
733
+ */
734
+ const streamed = isStreamedContentType(response.headers?.get?.("content-type"));
735
+
615
736
  // Clone response to read body with size limits
616
737
  const responseClone = response.clone();
617
738
  const {
618
739
  body,
619
740
  size: responseSize
620
- } = await self.processResponseBody(responseClone);
741
+ } = streamed ? {
742
+ body: STREAMING_PLACEHOLDER,
743
+ size: 0
744
+ } : await self.processResponseBody(responseClone);
621
745
 
622
746
  // Now the body is in — this is the comparable, total number.
623
747
  const duration = Date.now() - startTime;
@@ -676,6 +800,36 @@ class NetworkListener {
676
800
  },
677
801
  override
678
802
  });
803
+ if (streamed) {
804
+ // Bounded, cancelled at the cap, and never awaited by the caller.
805
+ void self.readStreamedBody(responseClone).then(late => {
806
+ self.emit({
807
+ type: "response",
808
+ timestamp: new Date(),
809
+ duration: Date.now() - startTime,
810
+ timings,
811
+ request: {
812
+ id: requestId || "unknown",
813
+ url: cleanUrl,
814
+ method,
815
+ headers: requestHeaders,
816
+ data: requestData,
817
+ params: params || undefined,
818
+ client: clientType
819
+ },
820
+ response: {
821
+ status: response.status,
822
+ statusText: response.statusText,
823
+ headers: responseHeaders,
824
+ body: late.body,
825
+ size: late.size
826
+ },
827
+ override
828
+ });
829
+ }).catch(() => {
830
+ /* the caller's response is unaffected either way */
831
+ });
832
+ }
679
833
  return response;
680
834
  } catch (error) {
681
835
  const duration = Date.now() - startTime;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { DynamicFilterView, macOSColors, FileText, FileCode, Link, Zap, Settings, Hash, Eye, ToolbarCopyButton, formatRelativeTime, useFeatureGate, ProFeatureBanner, ExportPreview } from "@buoy-gg/shared-ui";
3
+ import { DynamicFilterView, macOSColors, FileText, FileCode, Link, Zap, Settings, Hash, Eye, ToolbarCopyButton, formatRelativeTime, useFeatureGate, ProFeatureBanner, ExportPreview, monoFont } from "@buoy-gg/shared-ui";
4
4
  import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
5
5
  import { useCallback, useMemo, useState } from "react";
6
6
  import { Text, StyleSheet, ScrollView, View } from "react-native";
@@ -721,13 +721,13 @@ const styles = StyleSheet.create({
721
721
  maxHeight: 300
722
722
  },
723
723
  markdownText: {
724
- fontFamily: "monospace",
724
+ fontFamily: monoFont,
725
725
  fontSize: 11,
726
726
  color: macOSColors.text.primary,
727
727
  lineHeight: 18
728
728
  },
729
729
  plaintextText: {
730
- fontFamily: "monospace",
730
+ fontFamily: monoFont,
731
731
  fontSize: 11,
732
732
  color: macOSColors.text.primary,
733
733
  lineHeight: 18
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useState, useEffect, useMemo, memo } from "react";
4
4
  import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from "react-native";
5
- import { Clock, Upload, Download, AlertCircle, ChevronDown, ChevronUp, Lock, Unlock, FileJson, Filter, Globe, Link, Copy, FlaskConical, InlineCopyButton, macOSColors, night, useFeatureGate, UpgradeModal } from "@buoy-gg/shared-ui";
5
+ import { Clock, Upload, Download, AlertCircle, ChevronDown, ChevronUp, Lock, Unlock, FileJson, Filter, Globe, Link, Copy, FlaskConical, InlineCopyButton, macOSColors, night, useFeatureGate, UpgradeModal, monoFont } from "@buoy-gg/shared-ui";
6
6
  import { formatBytes, formatDuration, formatHttpStatus } from "../utils/formatting";
7
7
  import { formatRelativeTime } from "@buoy-gg/shared-ui";
8
8
  import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
@@ -869,12 +869,12 @@ const styles = StyleSheet.create({
869
869
  urlPath: {
870
870
  color: night.color.textSecondary,
871
871
  fontSize: 11,
872
- fontFamily: "monospace"
872
+ fontFamily: monoFont
873
873
  },
874
874
  urlQueryString: {
875
875
  color: night.color.info,
876
876
  fontSize: 11,
877
- fontFamily: "monospace"
877
+ fontFamily: monoFont
878
878
  },
879
879
  // Query Parameters - Collapsible section
880
880
  queryParamsCollapsible: {
@@ -951,7 +951,7 @@ const styles = StyleSheet.create({
951
951
  sizeValue: {
952
952
  color: night.color.info,
953
953
  fontSize: 10,
954
- fontFamily: "monospace",
954
+ fontFamily: monoFont,
955
955
  fontWeight: "600"
956
956
  },
957
957
  /* ---- Timing phases ---- */
@@ -996,7 +996,7 @@ const styles = StyleSheet.create({
996
996
  width: 72,
997
997
  textAlign: "right",
998
998
  fontSize: 12,
999
- fontFamily: "monospace",
999
+ fontFamily: monoFont,
1000
1000
  color: night.color.text
1001
1001
  },
1002
1002
  // Collapsible section styles
@@ -1032,7 +1032,7 @@ const styles = StyleSheet.create({
1032
1032
  collapsibleCount: {
1033
1033
  color: night.color.textTertiary,
1034
1034
  fontSize: 11.5,
1035
- fontFamily: "monospace"
1035
+ fontFamily: monoFont
1036
1036
  },
1037
1037
  collapsibleContent: {
1038
1038
  padding: 12
@@ -1106,7 +1106,7 @@ const styles = StyleSheet.create({
1106
1106
  filterOptionValue: {
1107
1107
  color: night.color.text,
1108
1108
  fontSize: 13,
1109
- fontFamily: "monospace"
1109
+ fontFamily: monoFont
1110
1110
  },
1111
1111
  filterToggle: {
1112
1112
  paddingHorizontal: 10,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { memo } from "react";
4
4
  import { StyleSheet, View, Text } from "react-native";
5
- import { ChevronRight, Upload, Download, Clock, AlertCircle, Bookmark, FlaskConical, Pin, DevToolsCard, MethodBadge, TypeBadge, night, macOSColors } from "@buoy-gg/shared-ui";
5
+ import { ChevronRight, Upload, Download, Clock, AlertCircle, Bookmark, FlaskConical, Pin, DevToolsCard, MethodBadge, TypeBadge, night, macOSColors, monoFont } from "@buoy-gg/shared-ui";
6
6
  import { formatBytes, formatDuration } from "../utils/formatting";
7
7
  import { formatRelativeTime } from "@buoy-gg/shared-ui";
8
8
  import { useTickEveryMinute } from "../hooks/useTickEveryMinute";
@@ -316,7 +316,7 @@ const styles = StyleSheet.create({
316
316
  fontSize: 12,
317
317
  color: night.color.text,
318
318
  lineHeight: 16,
319
- fontFamily: "monospace"
319
+ fontFamily: monoFont
320
320
  },
321
321
  rightSection: {
322
322
  flexDirection: "row",
@@ -331,7 +331,7 @@ const styles = StyleSheet.create({
331
331
  bottomRightText: {
332
332
  fontSize: 9,
333
333
  color: night.color.textTertiary,
334
- fontFamily: "monospace"
334
+ fontFamily: monoFont
335
335
  },
336
336
  statusBadge: {
337
337
  paddingHorizontal: 4,
@@ -387,7 +387,7 @@ const styles = StyleSheet.create({
387
387
  sizeText: {
388
388
  fontSize: 8,
389
389
  color: night.color.textSecondary,
390
- fontFamily: "monospace"
390
+ fontFamily: monoFont
391
391
  },
392
392
  clientText: {
393
393
  fontSize: 8,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { CheckCircle, Clock, DynamicFilterView, Film, FileJson, FileText, Filter, Globe, Image, macOSColors, night, Music, XCircle } from "@buoy-gg/shared-ui";
3
+ import { CheckCircle, Clock, DynamicFilterView, Film, FileJson, FileText, Filter, Globe, Image, macOSColors, night, Music, XCircle, monoFont } from "@buoy-gg/shared-ui";
4
4
  import { useCallback, useMemo, useState } from "react";
5
5
  import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -384,7 +384,7 @@ const styles = StyleSheet.create({
384
384
  },
385
385
  modeBadgeText: {
386
386
  fontSize: 9,
387
- fontFamily: "monospace",
387
+ fontFamily: monoFont,
388
388
  fontWeight: "700",
389
389
  letterSpacing: 0.5
390
390
  },
@@ -398,7 +398,7 @@ const styles = StyleSheet.create({
398
398
  fontSize: 11,
399
399
  fontWeight: "600",
400
400
  color: night.color.textSecondary,
401
- fontFamily: "monospace",
401
+ fontFamily: monoFont,
402
402
  letterSpacing: 0.3
403
403
  },
404
404
  modeToggleGroup: {
@@ -424,7 +424,7 @@ const styles = StyleSheet.create({
424
424
  fontSize: 11,
425
425
  fontWeight: "700",
426
426
  color: night.color.textSecondary,
427
- fontFamily: "monospace",
427
+ fontFamily: monoFont,
428
428
  letterSpacing: 0.3
429
429
  },
430
430
  modeToggleButtonTextActive: {
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
22
- import { Bookmark, CheckCircle, XCircle, Clock, Copy, Pin, Trash2, CopyButton, macOSColors, night, useLimit } from "@buoy-gg/shared-ui";
22
+ import { Bookmark, CheckCircle, XCircle, Clock, Copy, Pin, Trash2, CopyButton, macOSColors, night, useLimit, monoFont } from "@buoy-gg/shared-ui";
23
23
  import { networkEventStore } from "../utils/networkEventStore";
24
24
  import { flagsForEventId, networkSavedStore, MAX_PINNED, MAX_SAVED } from "../utils/networkSavedStore";
25
25
  import { useNetworkSaved } from "../hooks/useNetworkSaved";
@@ -322,7 +322,7 @@ const styles = StyleSheet.create({
322
322
  headerChipValue: {
323
323
  fontSize: 12,
324
324
  fontWeight: "600",
325
- fontFamily: "monospace"
325
+ fontFamily: monoFont
326
326
  },
327
327
  headerActionButton: {
328
328
  width: 32,
@@ -361,6 +361,6 @@ const styles = StyleSheet.create({
361
361
  color: night.color.onAccent,
362
362
  fontSize: 9,
363
363
  fontWeight: "700",
364
- fontFamily: "monospace"
364
+ fontFamily: monoFont
365
365
  }
366
366
  });
@@ -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, Pin, Power, macOSColors, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET } from "@buoy-gg/shared-ui";
17
+ import { FlaskConical, Pin, Power, macOSColors, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET, monoFont } from "@buoy-gg/shared-ui";
18
18
  import { NetworkEventItemCompact } from "./NetworkEventItemCompact";
19
19
  import { NetworkUpgradeBanner } from "./NetworkUpgradeBanner";
20
20
  import { useNetworkEventList } from "../hooks/useNetworkEventList";
@@ -367,7 +367,7 @@ const styles = StyleSheet.create({
367
367
  fontSize: 10,
368
368
  fontWeight: "700",
369
369
  letterSpacing: 0.6,
370
- fontFamily: "monospace"
370
+ fontFamily: monoFont
371
371
  },
372
372
  pinnedClear: {
373
373
  paddingHorizontal: 6,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useMemo, useState } from "react";
4
4
  import { ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
5
- import { ChevronDown, ChevronRight, macOSColors, useSafeAreaInsets } from "@buoy-gg/shared-ui";
5
+ import { ChevronDown, ChevronRight, macOSColors, useSafeAreaInsets, monoFont } from "@buoy-gg/shared-ui";
6
6
  import Explorer from "./response-editor/Explorer";
7
7
  import { ResponseEditorContext } from "./response-editor/editorContext";
8
8
  import { NetworkNumberPrompt } from "./NetworkNumberPrompt";
@@ -678,7 +678,7 @@ const styles = StyleSheet.create({
678
678
  paddingVertical: 8,
679
679
  color: macOSColors.text.primary,
680
680
  fontSize: 12,
681
- fontFamily: "monospace",
681
+ fontFamily: monoFont,
682
682
  minHeight: 40
683
683
  },
684
684
  bodyInput: {
@@ -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, MethodBadge, Plus, Trash2, macOSColors, useLimit } from "@buoy-gg/shared-ui";
5
+ import { AlertCircle, AlertTriangle, DevToolsCard, EmptyState, MethodBadge, Plus, Trash2, macOSColors, useLimit, monoFont } 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";
@@ -489,7 +489,7 @@ const styles = StyleSheet.create({
489
489
  fontSize: 12,
490
490
  color: macOSColors.text.primary,
491
491
  lineHeight: 16,
492
- fontFamily: "monospace"
492
+ fontFamily: monoFont
493
493
  },
494
494
  urlTextOff: {
495
495
  color: macOSColors.text.muted
@@ -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, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET } from "@buoy-gg/shared-ui";
22
+ import { Bookmark, CopyButton, Search, macOSColors, useLimit, ToolEmptyState, resolveListEmptyState, EVENT_LIST_PRESET, monoFont } from "@buoy-gg/shared-ui";
23
23
  import { NetworkEventItemCompact } from "./NetworkEventItemCompact";
24
24
  import { NetworkUpgradeBanner } from "./NetworkUpgradeBanner";
25
25
  import { generateNetworkCopyText } from "../utils/generateNetworkCopyText";
@@ -177,7 +177,7 @@ const styles = StyleSheet.create({
177
177
  fontSize: 10,
178
178
  fontWeight: "700",
179
179
  letterSpacing: 0.6,
180
- fontFamily: "monospace"
180
+ fontFamily: monoFont
181
181
  },
182
182
  toolbarButton: {
183
183
  width: 26,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useState, useMemo, useCallback, useRef, useEffect, memo } from "react";
4
4
  import { Text, TouchableOpacity, View, StyleSheet } from "react-native";
5
- import { displayValue, buoyColors } from "@buoy-gg/shared-ui";
5
+ import { displayValue, buoyColors, monoFont } from "@buoy-gg/shared-ui";
6
6
  import { CopyButton as SharedCopyButton } from "@buoy-gg/shared-ui";
7
7
  import { ChevronRight, ChevronDown, Trash } from "@buoy-gg/shared-ui";
8
8
  import { CyberpunkInput } from "@buoy-gg/shared-ui/dataViewer";
@@ -539,7 +539,7 @@ const styles = StyleSheet.create({
539
539
  clearButtonText: {
540
540
  fontSize: 12,
541
541
  fontWeight: "700",
542
- fontFamily: "monospace"
542
+ fontFamily: monoFont
543
543
  },
544
544
  expanderIcon: {
545
545
  width: 18,
@@ -626,7 +626,7 @@ const styles = StyleSheet.create({
626
626
  fontSize: 10,
627
627
  fontWeight: "600",
628
628
  marginRight: 4,
629
- fontFamily: "monospace",
629
+ fontFamily: monoFont,
630
630
  letterSpacing: 0.4
631
631
  },
632
632
  labelTextFocused: {
@@ -642,14 +642,14 @@ const styles = StyleSheet.create({
642
642
  color: buoyColors.textMuted,
643
643
  fontSize: 10,
644
644
  fontWeight: "400",
645
- fontFamily: "monospace",
645
+ fontFamily: monoFont,
646
646
  opacity: 0.7
647
647
  },
648
648
  pageRangeText: {
649
649
  color: buoyColors.textSecondary,
650
650
  fontSize: 10,
651
651
  fontWeight: "600",
652
- fontFamily: "monospace"
652
+ fontFamily: monoFont
653
653
  },
654
654
  flexRowGapItemsCenter: {
655
655
  flexDirection: "row",
@@ -707,11 +707,11 @@ const styles = StyleSheet.create({
707
707
  textNumber: {
708
708
  color: buoyColors.text,
709
709
  fontWeight: "600",
710
- fontFamily: "monospace"
710
+ fontFamily: monoFont
711
711
  },
712
712
  textString: {
713
713
  color: buoyColors.text,
714
- fontFamily: "monospace"
714
+ fontFamily: monoFont
715
715
  },
716
716
  flexRowGapFullWidth: {
717
717
  flexDirection: "row",
@@ -728,7 +728,7 @@ const styles = StyleSheet.create({
728
728
  fontWeight: "600",
729
729
  fontSize: 9,
730
730
  minWidth: 50,
731
- fontFamily: "monospace",
731
+ fontFamily: monoFont,
732
732
  letterSpacing: 0.4,
733
733
  opacity: 0.8
734
734
  },
@@ -782,7 +782,7 @@ const styles = StyleSheet.create({
782
782
  flex: 1,
783
783
  color: buoyColors.text,
784
784
  fontWeight: "400",
785
- fontFamily: "monospace",
785
+ fontFamily: monoFont,
786
786
  fontSize: 12,
787
787
  paddingVertical: 6,
788
788
  paddingHorizontal: 10,
@@ -838,7 +838,7 @@ const styles = StyleSheet.create({
838
838
  readOnlyValue: {
839
839
  color: buoyColors.text,
840
840
  fontSize: 13,
841
- fontFamily: "monospace",
841
+ fontFamily: monoFont,
842
842
  paddingVertical: 8,
843
843
  paddingHorizontal: 12,
844
844
  borderRadius: 6,
@@ -864,7 +864,7 @@ const styles = StyleSheet.create({
864
864
  marginLeft: 8,
865
865
  color: buoyColors.warning,
866
866
  fontWeight: "500",
867
- fontFamily: "monospace"
867
+ fontFamily: monoFont
868
868
  },
869
869
  modernToggleButton: {
870
870
  flexDirection: "row",
@@ -903,7 +903,7 @@ const styles = StyleSheet.create({
903
903
  color: "#E5E7EB",
904
904
  fontSize: 11,
905
905
  fontWeight: "600",
906
- fontFamily: "monospace",
906
+ fontFamily: monoFont,
907
907
  letterSpacing: 0.3
908
908
  },
909
909
  toggleStatus: {
@@ -922,7 +922,7 @@ const styles = StyleSheet.create({
922
922
  fontWeight: "700",
923
923
  textTransform: "uppercase",
924
924
  letterSpacing: 0.8,
925
- fontFamily: "monospace"
925
+ fontFamily: monoFont
926
926
  },
927
927
  // Pre-computed toggle icon styles to avoid inline objects [[memory:4875251]]
928
928
  toggleIconTrue: {
@@ -961,7 +961,7 @@ const styles = StyleSheet.create({
961
961
  color: buoyColors.textSecondary,
962
962
  fontSize: 10,
963
963
  fontWeight: "600",
964
- fontFamily: "monospace",
964
+ fontFamily: monoFont,
965
965
  letterSpacing: 0.4,
966
966
  minWidth: 60,
967
967
  flexShrink: 0