@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,133 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NetworkOverrideHeaderButton = NetworkOverrideHeaderButton;
7
+ var _useOverrideRules = require("../hooks/useOverrideRules");
8
+ var _useNetworkSelection = require("../hooks/useNetworkSelection");
9
+ var _matchRule = require("../overrides/matchRule");
10
+ var _presets = require("../overrides/presets");
11
+ var _networkEventStore = require("../utils/networkEventStore");
12
+ var _networkSavedStore = require("../utils/networkSavedStore");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ /**
15
+ * NetworkOverrideHeaderButton.web — DOM twin of
16
+ * NetworkOverrideHeaderButton.tsx.
17
+ *
18
+ * The Override action, in the request-detail header. A labelled word rather
19
+ * than an icon — it's the least familiar action in the tool, and "Override"
20
+ * spelled out is the entire explanation. A self-subscribing leaf: the
21
+ * selected id and the rule list are subscribed HERE so stepping between
22
+ * requests doesn't rebuild the header around a new prop.
23
+ *
24
+ * It lights up while a rule covers the request you're looking at — the same
25
+ * warning tint the OVERRIDDEN strip and the toolbar flask use. Tapping it
26
+ * goes straight to the Overrides screen with the rule open: a new one
27
+ * prefilled from this request (`draftFromEvent`), or the existing one if
28
+ * something already covers it (first enabled match wins, so stacking a
29
+ * second rule the first would shadow is never offered). (Native comments.)
30
+ *
31
+ * REUSED, not forked (all platform-free): `useOverrideRules`,
32
+ * `useSelectedNetworkEventId`, `ruleMatches`, `draftFromEvent`,
33
+ * `networkEventStore`, `networkSavedStore`.
34
+ *
35
+ * shared-ui RN chrome is replicated as DOM with provenance:
36
+ * - `macOSColors` — inlined verbatim subset from
37
+ * shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts.
38
+ *
39
+ * @platform React DOM Web
40
+ */
41
+
42
+ /** macOSColors subset — shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts, verbatim. */const macOSColors = {
43
+ background: {
44
+ input: "#26262A"
45
+ },
46
+ border: {
47
+ input: "#3D3D42"
48
+ },
49
+ text: {
50
+ primary: "#F5F5F7"
51
+ },
52
+ semantic: {
53
+ warning: "#FFEB3B",
54
+ warningBackground: "rgba(255, 235, 59, 0.15)"
55
+ }
56
+ };
57
+ function NetworkOverrideHeaderButton({
58
+ onOpen
59
+ }) {
60
+ const eventId = (0, _useNetworkSelection.useSelectedNetworkEventId)();
61
+ const {
62
+ rules,
63
+ isRuleSpent
64
+ } = (0, _useOverrideRules.useOverrideRules)();
65
+
66
+ // Resolved here rather than held as a prop: the live store has the fresh
67
+ // copy while a request is in flight, and the saved store is the only copy
68
+ // once it has been cleared or aged out. (Native comment.)
69
+ const event = eventId ? _networkEventStore.networkEventStore.getEvents().find(e => e.id === eventId) ?? _networkSavedStore.networkSavedStore.getEventById(eventId) : null;
70
+ const existing = event ? rules.find(rule => rule.enabled && !isRuleSpent(rule) && (0, _matchRule.ruleMatches)(rule, event.url, String(event.method || "GET"))) ?? null : null;
71
+ const active = existing !== null;
72
+ const handlePress = () => {
73
+ if (!event) return;
74
+ if (existing) {
75
+ // Editing what's already there, rather than stacking a second rule the
76
+ // first would shadow — first enabled match wins.
77
+ const {
78
+ id,
79
+ hits: _h,
80
+ seen: _s,
81
+ createdAt: _c,
82
+ ...draft
83
+ } = existing;
84
+ onOpen(draft, id);
85
+ return;
86
+ }
87
+ onOpen((0, _presets.draftFromEvent)(event));
88
+ };
89
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
90
+ type: "button",
91
+ onClick: handlePress,
92
+ "aria-label": active ? "Edit the override on this request" : "Override this request",
93
+ style: {
94
+ ...styles.button,
95
+ ...(active ? styles.buttonActive : null)
96
+ },
97
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
98
+ style: {
99
+ ...styles.label,
100
+ ...(active ? styles.labelActive : null)
101
+ },
102
+ children: "Override"
103
+ })
104
+ });
105
+ }
106
+
107
+ // Values mirror the native StyleSheet block 1:1.
108
+ const styles = {
109
+ button: {
110
+ display: "inline-flex",
111
+ alignItems: "center",
112
+ padding: "6px 10px",
113
+ borderRadius: 6,
114
+ border: `1px solid ${macOSColors.border.input}`,
115
+ backgroundColor: macOSColors.background.input,
116
+ cursor: "pointer",
117
+ margin: 0,
118
+ boxSizing: "border-box"
119
+ },
120
+ buttonActive: {
121
+ border: `1px solid ${macOSColors.semantic.warning}77`,
122
+ backgroundColor: macOSColors.semantic.warningBackground
123
+ },
124
+ label: {
125
+ fontSize: 11,
126
+ fontWeight: 700,
127
+ color: macOSColors.text.primary,
128
+ fontFamily: "inherit"
129
+ },
130
+ labelActive: {
131
+ color: macOSColors.semantic.warning
132
+ }
133
+ };
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NetworkOverridesButton = NetworkOverridesButton;
7
+ var _useOverrideRules = require("../hooks/useOverrideRules");
8
+ var _jsxRuntime = require("react/jsx-runtime");
9
+ /**
10
+ * NetworkOverridesButton.web — DOM twin of NetworkOverridesButton.tsx.
11
+ *
12
+ * Header button for the Overrides screen, and the always-visible "overrides
13
+ * are active" indicator. A self-subscribing LEAF, like the other live header
14
+ * pieces — the rule list changes on its own schedule (every hit bumps a
15
+ * counter) and the modal shell has no business re-rendering for it.
16
+ *
17
+ * The active tint is not decoration: a rule left enabled from yesterday makes
18
+ * the app behave wrongly in a way that looks exactly like a real bug, so the
19
+ * tool keeps saying so from the top level. (Native comment.)
20
+ *
21
+ * REUSED, not forked: `useOverrideRules` (platform-free).
22
+ *
23
+ * shared-ui RN chrome is replicated as DOM with provenance:
24
+ * - `macOSColors` — inlined verbatim subset from
25
+ * shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts.
26
+ * - `FlaskConical` — inline lucide SVG (shared-ui icons are lucide
27
+ * re-exports).
28
+ *
29
+ * @platform React DOM Web
30
+ */
31
+
32
+ /** macOSColors subset — shared/src/ui/gameUI/constants/macOSDesignSystemColors.ts, verbatim. */const macOSColors = {
33
+ background: {
34
+ base: "#0A0A0C",
35
+ hover: "#1D1D1F"
36
+ },
37
+ border: {
38
+ default: "#2D2D2F"
39
+ },
40
+ text: {
41
+ secondary: "#A1A1A6"
42
+ },
43
+ semantic: {
44
+ warning: "#FFEB3B",
45
+ warningBackground: "rgba(255, 235, 59, 0.15)"
46
+ }
47
+ };
48
+ const FlaskConical = ({
49
+ size = 14,
50
+ color
51
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("svg", {
52
+ width: size,
53
+ height: size,
54
+ viewBox: "0 0 24 24",
55
+ fill: "none",
56
+ stroke: color,
57
+ strokeWidth: 2,
58
+ strokeLinecap: "round",
59
+ strokeLinejoin: "round",
60
+ style: {
61
+ flexShrink: 0,
62
+ display: "block"
63
+ },
64
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
65
+ d: "M10 2v7.527a2 2 0 0 1-.211.896L4.72 20.55a1 1 0 0 0 .9 1.45h12.76a1 1 0 0 0 .9-1.45l-5.069-10.127A2 2 0 0 1 14 9.527V2"
66
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
67
+ d: "M8.5 2h7"
68
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
69
+ d: "M7 16h10"
70
+ })]
71
+ });
72
+ function NetworkOverridesButton({
73
+ onPress
74
+ }) {
75
+ const {
76
+ activeCount
77
+ } = (0, _useOverrideRules.useOverrideRules)();
78
+ const active = activeCount > 0;
79
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
80
+ type: "button",
81
+ onClick: onPress,
82
+ style: {
83
+ ...styles.button,
84
+ ...(active ? styles.buttonActive : null)
85
+ },
86
+ "aria-label": active ? `Response overrides — ${activeCount} active` : "Response overrides",
87
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(FlaskConical, {
88
+ size: 14,
89
+ color: active ? macOSColors.semantic.warning : macOSColors.text.secondary
90
+ }), active ? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
91
+ style: styles.badge,
92
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
93
+ style: styles.badgeText,
94
+ children: activeCount
95
+ })
96
+ }) : null]
97
+ });
98
+ }
99
+
100
+ // Values mirror the native StyleSheet block 1:1: the header's standard 32×32
101
+ // action box; active fills the whole box the way the power toggle goes green,
102
+ // with the count badge INSIDE the box.
103
+ const styles = {
104
+ button: {
105
+ position: "relative",
106
+ display: "inline-flex",
107
+ width: 32,
108
+ height: 32,
109
+ boxSizing: "border-box",
110
+ borderRadius: 8,
111
+ alignItems: "center",
112
+ justifyContent: "center",
113
+ backgroundColor: macOSColors.background.hover,
114
+ border: `1px solid ${macOSColors.border.default}`,
115
+ cursor: "pointer",
116
+ padding: 0,
117
+ margin: 0
118
+ },
119
+ buttonActive: {
120
+ backgroundColor: macOSColors.semantic.warningBackground,
121
+ border: `1px solid ${macOSColors.semantic.warning}77`
122
+ },
123
+ badge: {
124
+ position: "absolute",
125
+ top: 1,
126
+ right: 1,
127
+ minWidth: 12,
128
+ height: 12,
129
+ borderRadius: 6,
130
+ padding: "0 3px",
131
+ boxSizing: "border-box",
132
+ display: "inline-flex",
133
+ alignItems: "center",
134
+ justifyContent: "center",
135
+ backgroundColor: macOSColors.semantic.warning
136
+ },
137
+ badgeText: {
138
+ fontSize: 8,
139
+ fontWeight: 800,
140
+ color: macOSColors.background.base,
141
+ lineHeight: 1
142
+ }
143
+ };
@@ -41,101 +41,111 @@ function toDraft(rule) {
41
41
  return draft;
42
42
  }
43
43
 
44
- /** Plain-language names for statuses, so a row never reads as a bare number. */
45
- const STATUS_NAMES = {
46
- 200: "Success",
47
- 201: "Created",
48
- 400: "Bad request",
49
- 401: "Unauthorized",
50
- 403: "Forbidden",
51
- 404: "Not found",
52
- 429: "Rate limited",
53
- 500: "Server error",
54
- 503: "Unavailable"
55
- };
56
-
57
- /** What the rule DOES, in the same words the editor uses. */
58
- function outcomeLine(rule) {
59
- const parts = [];
60
- if (rule.kind === "respond") {
61
- const status = rule.status ?? 200;
62
- const name = STATUS_NAMES[status];
63
- parts.push(name ? `${name} ${status}` : String(status));
64
- } else if (rule.kind === "fail") {
65
- parts.push(rule.failKind === "timeout" ? "Timeout" : "Offline");
66
- } else {
67
- parts.push("Real response");
68
- }
69
- if (rule.delayMs) parts.push(`${rule.delayMs / 1000}s`);
70
- if (rule.alternate) parts.push("every other");else if (rule.times === 1) parts.push("once");else if (typeof rule.times === "number") parts.push(`${rule.times}×`);
71
- return parts.join(" · ");
72
- }
73
-
74
44
  /**
75
45
  * Where the rule applies — PATH only, not the whole URL.
76
46
  *
77
47
  * The host is the same for every rule in a typical app, so showing it pushes
78
- * the part that differs off the end of the line.
48
+ * the part that differs off the end of the line. Same reasoning as the request
49
+ * row's `displayUrl`.
79
50
  */
80
- function scopeLine(rule) {
81
- const method = rule.methods?.length ? rule.methods.join("/") : "Any";
82
- let path = rule.urlPattern;
51
+ function patternPath(rule) {
52
+ const path = rule.urlPattern;
83
53
  const schemeEnd = path.indexOf("://");
84
- if (schemeEnd !== -1) {
85
- const slash = path.indexOf("/", schemeEnd + 3);
86
- path = slash === -1 ? path.slice(schemeEnd + 3) : path.slice(slash);
87
- }
88
- return `${method} ${path}`;
54
+ if (schemeEnd === -1) return path;
55
+ const slash = path.indexOf("/", schemeEnd + 3);
56
+ return slash === -1 ? path.slice(schemeEnd + 3) : path.slice(slash);
57
+ }
58
+
59
+ /** Delay and lifetime, in the editor's own words. Empty for a plain rule. */
60
+ function extrasLine(rule) {
61
+ const parts = [];
62
+ if (rule.delayMs) parts.push(`${rule.delayMs / 1000}s delay`);
63
+ if (rule.alternate) parts.push("every other");else if (rule.times === 1) parts.push("once");else if (typeof rule.times === "number") parts.push(`${rule.times}×`);
64
+ return parts.join(" · ");
65
+ }
66
+
67
+ /** The outcome's color, on the request list's status scale — a forced 500
68
+ * should look exactly like a real one. */
69
+ function ruleAccent(rule) {
70
+ if (rule.kind === "fail") return _sharedUi.macOSColors.semantic.error;
71
+ if (rule.kind === "delay") return _sharedUi.macOSColors.semantic.info;
72
+ const status = rule.status ?? 200;
73
+ if (status >= 200 && status < 300) return _sharedUi.macOSColors.semantic.success;
74
+ if (status >= 300 && status < 400) return _sharedUi.macOSColors.semantic.info;
75
+ return _sharedUi.macOSColors.semantic.error;
89
76
  }
90
77
 
91
78
  /**
92
- * One rule. Toggle · two lines · hit count, and nothing else.
93
- *
94
- * The pencil and trash icons are gone: the row itself opens the editor (where
95
- * Delete now lives), and three tap targets in a 44pt row is a config table, not
96
- * a list. `hits` stays as the only right-hand element because "my glob never
97
- * matched" is the most common rule bug and the count is how you find out.
79
+ * One rule, in the request row's visual language — the same card, method badge,
80
+ * monospace path and status colors as `NetworkEventItemCompact`, so a rule
81
+ * reads as "the request it will produce". What's different is what a rule
82
+ * needs: a trash instead of the type chip and chevron, and the outcome color
83
+ * only floods the left edge while the rule is live. There's no per-rule
84
+ * on/off — a rule you don't want anymore gets deleted, and pausing everything
85
+ * is the master bar's job.
98
86
  */
99
87
  function RuleRow({
100
88
  rule,
101
89
  spent,
102
- onToggle,
103
- onEdit
90
+ onEdit,
91
+ onDelete
104
92
  }) {
105
93
  const live = rule.enabled && !spent;
106
- const accent = live ? _sharedUi.macOSColors.semantic.warning : _sharedUi.macOSColors.text.muted;
107
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
108
- style: styles.row,
109
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
110
- onPress: onToggle,
111
- "sentry-label": "ignore toggle override rule",
112
- accessibilityLabel: live ? "Turn rule off" : "Turn rule on",
113
- style: [styles.toggle, live && {
114
- borderColor: accent,
115
- backgroundColor: `${accent}33`
116
- }],
117
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
118
- style: [styles.toggleDot, {
119
- backgroundColor: accent
120
- }]
121
- })
122
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
123
- onPress: onEdit,
124
- "sentry-label": "ignore edit override rule",
125
- style: styles.rowBody,
94
+ const accent = ruleAccent(rule);
95
+ const extras = extrasLine(rule);
96
+ const method = rule.methods?.length ? rule.methods.join("/") : "ANY";
97
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.DevToolsCard, {
98
+ onPress: onEdit,
99
+ style: styles.cardWrapper,
100
+ contentStyle: [styles.card, {
101
+ borderLeftColor: live ? accent : "transparent"
102
+ }],
103
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.MethodBadge, {
104
+ method: method,
105
+ size: "small"
106
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
107
+ style: styles.cardMiddle,
126
108
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
127
- style: [styles.rowTitle, !live && styles.rowTitleOff],
128
- numberOfLines: 1,
129
- children: outcomeLine(rule)
130
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
131
- style: styles.rowScope,
132
- numberOfLines: 1,
133
- children: scopeLine(rule)
134
- })]
109
+ style: [styles.urlText, !live && styles.urlTextOff],
110
+ numberOfLines: 2,
111
+ children: patternPath(rule)
112
+ }), extras ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
113
+ style: styles.extrasText,
114
+ children: extras
115
+ }) : null]
135
116
  }), rule.hits > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
136
117
  style: styles.rowHits,
137
118
  children: rule.hits
138
- }) : null]
119
+ }) : null, rule.kind === "fail" ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
120
+ style: styles.failBadge,
121
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertCircle, {
122
+ size: 10,
123
+ color: _sharedUi.macOSColors.semantic.error
124
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
125
+ style: styles.failText,
126
+ children: rule.failKind === "timeout" ? "TIMEOUT" : "OFFLINE"
127
+ })]
128
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
129
+ style: [styles.statusText, {
130
+ color: accent
131
+ }],
132
+ children: rule.kind === "delay" ? "PASS" : String(rule.status ?? 200)
133
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
134
+ onPress: onDelete,
135
+ "sentry-label": "ignore delete override rule",
136
+ accessibilityLabel: "Delete rule",
137
+ hitSlop: {
138
+ top: 8,
139
+ bottom: 8,
140
+ left: 4,
141
+ right: 4
142
+ },
143
+ style: styles.rowDelete,
144
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
145
+ size: 13,
146
+ color: _sharedUi.macOSColors.text.muted
147
+ })
148
+ })]
139
149
  });
140
150
  }
141
151
  /**
@@ -159,6 +169,7 @@ function OverridesBody({
159
169
  onUpgradePress,
160
170
  pendingDraft,
161
171
  onPendingConsumed,
172
+ backHandlerRef,
162
173
  applySafeAreaInset
163
174
  }) {
164
175
  const {
@@ -180,6 +191,20 @@ function OverridesBody({
180
191
  _overrideRulesStore.overrideRulesStore.noteHealthyLaunch();
181
192
  }, []);
182
193
 
194
+ // Back while editing means "back to the rule list", not "leave overrides" —
195
+ // see the prop's comment. Cleared on unmount so a hidden screen can't
196
+ // swallow back presses.
197
+ (0, _react.useEffect)(() => {
198
+ if (!backHandlerRef) return;
199
+ backHandlerRef.current = editing ? () => {
200
+ setEditing(null);
201
+ return true;
202
+ } : null;
203
+ return () => {
204
+ backHandlerRef.current = null;
205
+ };
206
+ }, [backHandlerRef, editing]);
207
+
183
208
  /**
184
209
  * Open a rule for editing, pulling its body over if this surface doesn't have
185
210
  * it.
@@ -255,8 +280,12 @@ function OverridesBody({
255
280
  };
256
281
  const handleSave = () => {
257
282
  if (!editing) return;
283
+ // Saving always arms the rule. With no per-rule toggle, this is the ONLY
284
+ // way back on for a rule the free tier disabled or that used itself up —
285
+ // and nobody edits a rule they want to stay inert.
258
286
  const draft = {
259
287
  ...editing.draft,
288
+ enabled: true,
260
289
  urlPattern: editing.draft.urlPattern.trim()
261
290
  };
262
291
  if (!draft.urlPattern) return;
@@ -264,21 +293,14 @@ function OverridesBody({
264
293
  ...draft,
265
294
  id: editing.id
266
295
  });
267
- if (draft.enabled) {
268
- enforceFreeTier(saved?.id ?? editing.id);
269
- // Arm the master switch. Deliberately creating a rule and having it sit
270
- // inert because overrides happen to be paused is the silent-failure this
271
- // feature can least afford — the preset path already does this, and the
272
- // manager disagreeing with it was just a gap.
273
- setEnabled(true);
274
- }
296
+ enforceFreeTier(saved?.id ?? editing.id);
297
+ // Arm the master switch. Deliberately creating a rule and having it sit
298
+ // inert because overrides happen to be paused is the silent-failure this
299
+ // feature can least afford — the preset path already does this, and the
300
+ // manager disagreeing with it was just a gap.
301
+ setEnabled(true);
275
302
  setEditing(null);
276
303
  };
277
- const handleToggle = rule => {
278
- const next = !rule.enabled;
279
- setRuleEnabled(rule.id, next);
280
- if (next) enforceFreeTier(rule.id);
281
- };
282
304
  if (editing) {
283
305
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_NetworkOverrideEditor.NetworkOverrideEditor, {
284
306
  draft: editing.draft,
@@ -348,12 +370,12 @@ function OverridesBody({
348
370
  children: [rules.map(rule => /*#__PURE__*/(0, _jsxRuntime.jsx)(RuleRow, {
349
371
  rule: rule,
350
372
  spent: isRuleSpent(rule),
351
- onToggle: () => handleToggle(rule),
352
- onEdit: () => openRule(rule)
353
- }, rule.id)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
373
+ onEdit: () => openRule(rule),
374
+ onDelete: () => removeRule(rule.id)
375
+ }, rule.id)), !isPro ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
354
376
  style: styles.footNote,
355
- children: isPro ? "Dev builds only." : "Dev builds only. Free: one override at a time. Pro: up to 50."
356
- })]
377
+ children: "Free: one override at a time. Pro: up to 50."
378
+ }) : null]
357
379
  })]
358
380
  });
359
381
  }
@@ -430,54 +452,66 @@ const styles = _reactNative.StyleSheet.create({
430
452
  padding: 10,
431
453
  gap: 6
432
454
  },
433
- row: {
434
- flexDirection: "row",
435
- alignItems: "center",
436
- gap: 10,
437
- paddingHorizontal: 10,
438
- paddingVertical: 10,
439
- borderRadius: 6,
440
- borderWidth: 1,
441
- borderColor: _sharedUi.macOSColors.border.default,
442
- backgroundColor: _sharedUi.macOSColors.background.card
455
+ // The list already spaces rows via `listContent` gap — zero out the card's
456
+ // own margins so the two don't stack.
457
+ cardWrapper: {
458
+ marginHorizontal: 0,
459
+ marginVertical: 0
443
460
  },
444
- toggle: {
445
- width: 22,
446
- height: 22,
447
- borderRadius: 11,
448
- borderWidth: 1,
449
- borderColor: _sharedUi.macOSColors.border.default,
461
+ card: {
462
+ flexDirection: "row",
450
463
  alignItems: "center",
451
- justifyContent: "center"
452
- },
453
- toggleDot: {
454
- width: 8,
455
- height: 8,
456
- borderRadius: 4
464
+ gap: 8,
465
+ borderLeftWidth: 3,
466
+ borderLeftColor: "transparent"
457
467
  },
458
- rowBody: {
468
+ cardMiddle: {
459
469
  flex: 1
460
470
  },
461
- rowTitle: {
471
+ // Same type as the request row's URL.
472
+ urlText: {
462
473
  fontSize: 12,
463
- fontWeight: "600",
464
- color: _sharedUi.macOSColors.text.primary
474
+ color: _sharedUi.macOSColors.text.primary,
475
+ lineHeight: 16,
476
+ fontFamily: "monospace"
465
477
  },
466
- rowTitleOff: {
478
+ urlTextOff: {
467
479
  color: _sharedUi.macOSColors.text.muted
468
480
  },
469
- rowScope: {
481
+ extrasText: {
470
482
  fontSize: 10,
471
- fontFamily: "monospace",
472
483
  color: _sharedUi.macOSColors.text.secondary,
473
484
  marginTop: 2
474
485
  },
486
+ statusText: {
487
+ fontSize: 11,
488
+ fontWeight: "600"
489
+ },
490
+ failBadge: {
491
+ flexDirection: "row",
492
+ alignItems: "center",
493
+ gap: 2,
494
+ paddingHorizontal: 4,
495
+ paddingVertical: 1,
496
+ backgroundColor: _sharedUi.macOSColors.semantic.error + "26",
497
+ borderRadius: 3
498
+ },
499
+ failText: {
500
+ fontSize: 10,
501
+ color: _sharedUi.macOSColors.semantic.error,
502
+ fontWeight: "600"
503
+ },
475
504
  rowHits: {
476
505
  fontSize: 11,
477
506
  fontWeight: "700",
478
- color: _sharedUi.macOSColors.semantic.warning,
479
- minWidth: 16,
480
- textAlign: "right"
507
+ color: _sharedUi.macOSColors.semantic.warning
508
+ },
509
+ rowDelete: {
510
+ padding: 5,
511
+ borderRadius: 5,
512
+ borderWidth: 1,
513
+ borderColor: _sharedUi.macOSColors.border.default,
514
+ backgroundColor: _sharedUi.macOSColors.background.base
481
515
  },
482
516
  footNote: {
483
517
  fontSize: 10,