@outcode/bug-reporter-native 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,805 @@
1
+ 'use strict';
2
+
3
+ var React2 = require('react');
4
+ var reactNative = require('react-native');
5
+ var Svg3 = require('react-native-svg');
6
+ var bugReporterCore = require('@outcode/bug-reporter-core');
7
+ var reactNativeSafeAreaContext = require('react-native-safe-area-context');
8
+ var reactNativeViewShot = require('react-native-view-shot');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var React2__default = /*#__PURE__*/_interopDefault(React2);
14
+ var Svg3__default = /*#__PURE__*/_interopDefault(Svg3);
15
+
16
+ // src/BugReporterButton.tsx
17
+ function AnnotateToolbar({ t, tool, color, onTool, onColor, onUndo }) {
18
+ return /* @__PURE__ */ jsxRuntime.jsx(
19
+ reactNative.View,
20
+ {
21
+ style: [
22
+ styles.bar,
23
+ { backgroundColor: t.panel, borderColor: t.border, shadowColor: "#000" }
24
+ ],
25
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.row, children: [
26
+ bugReporterCore.ANNOTATION_TOOLS.map((tl) => {
27
+ const active = tool === tl.id;
28
+ return /* @__PURE__ */ jsxRuntime.jsx(
29
+ reactNative.TouchableOpacity,
30
+ {
31
+ onPress: () => onTool(tl.id),
32
+ style: [styles.iconBtn, { backgroundColor: active ? t.ring : "transparent" }],
33
+ children: /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: tl.iconPath, stroke: active ? t.accent : t.muted, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) })
34
+ },
35
+ tl.id
36
+ );
37
+ }),
38
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.divider, { backgroundColor: t.border }] }),
39
+ bugReporterCore.ANNOTATION_COLORS.map((c) => {
40
+ const active = color === c;
41
+ return /* @__PURE__ */ jsxRuntime.jsx(
42
+ reactNative.TouchableOpacity,
43
+ {
44
+ onPress: () => onColor(c),
45
+ style: {
46
+ width: active ? 22 : 20,
47
+ height: active ? 22 : 20,
48
+ borderRadius: 11,
49
+ marginHorizontal: 3,
50
+ backgroundColor: c,
51
+ borderWidth: 2,
52
+ borderColor: active ? t.accent : t.border
53
+ }
54
+ },
55
+ c
56
+ );
57
+ }),
58
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.divider, { backgroundColor: t.border }] }),
59
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onUndo, style: styles.iconBtn, children: /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-3", stroke: t.muted, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }) })
60
+ ] })
61
+ }
62
+ );
63
+ }
64
+ var styles = reactNative.StyleSheet.create({
65
+ bar: {
66
+ borderWidth: 1,
67
+ borderRadius: 14,
68
+ paddingVertical: 7,
69
+ paddingHorizontal: 7,
70
+ shadowOffset: { width: 0, height: 12 },
71
+ shadowOpacity: 0.28,
72
+ shadowRadius: 24,
73
+ elevation: 10
74
+ },
75
+ row: { flexDirection: "row", alignItems: "center", gap: 2 },
76
+ iconBtn: { width: 36, height: 36, borderRadius: 10, alignItems: "center", justifyContent: "center" },
77
+ divider: { width: 1, height: 24, marginHorizontal: 6 }
78
+ });
79
+ var HEADER_H = 50;
80
+ function pointsStr(points) {
81
+ return points.map((p) => `${p.x},${p.y}`).join(" ");
82
+ }
83
+ function hitTestText(shapes, p) {
84
+ for (let i = shapes.length - 1; i >= 0; i--) {
85
+ const sh = shapes[i];
86
+ if (sh.type !== "text") continue;
87
+ const w = Math.max(48, sh.text.length * 8 + 24);
88
+ if (p.x >= sh.x - 8 && p.x <= sh.x + w && p.y >= sh.y - 20 && p.y <= sh.y + 10) return sh.id;
89
+ }
90
+ return null;
91
+ }
92
+ function AnnotateOverlay({ t, visible, src, onCancel, onContinue }) {
93
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
94
+ const stageContainerRef = React2.useRef(null);
95
+ const idRef = React2.useRef(0);
96
+ const [shapes, setShapes] = React2.useState([]);
97
+ const [draft, setDraft] = React2.useState(null);
98
+ const [tool, setTool] = React2.useState("arrow");
99
+ const [color, setColor] = React2.useState(bugReporterCore.ANNOTATION_COLORS[0]);
100
+ const [editingId, setEditingId] = React2.useState(null);
101
+ const [dims, setDims] = React2.useState({ width: 320, height: 480 });
102
+ const toolRef = React2.useRef(tool);
103
+ const colorRef = React2.useRef(color);
104
+ const shapesRef = React2.useRef(shapes);
105
+ const startRef = React2.useRef(null);
106
+ toolRef.current = tool;
107
+ colorRef.current = color;
108
+ shapesRef.current = shapes;
109
+ React2.useEffect(() => {
110
+ if (!visible || !src) return;
111
+ reactNative.Image.getSize(
112
+ src,
113
+ (w, h) => {
114
+ const win = reactNative.Dimensions.get("window");
115
+ const maxW = win.width - 32;
116
+ const maxH = win.height - 230;
117
+ const aspect = w / h || 0.6;
118
+ let dw = maxW;
119
+ let dh = Math.round(maxW / aspect);
120
+ if (dh > maxH) {
121
+ dh = maxH;
122
+ dw = Math.round(maxH * aspect);
123
+ }
124
+ setDims({ width: dw, height: dh });
125
+ },
126
+ () => setDims({ width: reactNative.Dimensions.get("window").width - 32, height: 480 })
127
+ );
128
+ }, [visible, src]);
129
+ const startDraw = React2.useCallback((x, y) => {
130
+ const tl = toolRef.current;
131
+ const c = colorRef.current;
132
+ if (tl === "text") {
133
+ const hit = hitTestText(shapesRef.current, { x, y });
134
+ if (hit) {
135
+ setEditingId(hit);
136
+ return;
137
+ }
138
+ const id = `s${++idRef.current}`;
139
+ setShapes((s) => [...s, { id, type: "text", x, y, color: c, text: "" }]);
140
+ setEditingId(id);
141
+ return;
142
+ }
143
+ setEditingId(null);
144
+ startRef.current = { x, y };
145
+ setDraft(
146
+ tl === "pen" ? { id: `s${++idRef.current}`, type: "pen", color: c, points: [{ x, y }] } : { id: `s${++idRef.current}`, type: tl, color: c, x0: x, y0: y, x1: x, y1: y }
147
+ );
148
+ }, []);
149
+ const moveDraw = React2.useCallback((x, y) => {
150
+ setDraft((prev) => {
151
+ if (!prev) return prev;
152
+ if (prev.type === "pen") return { ...prev, points: [...prev.points, { x, y }] };
153
+ if (prev.type === "text") return prev;
154
+ return { ...prev, x1: x, y1: y };
155
+ });
156
+ }, []);
157
+ const endDraw = React2.useCallback(() => {
158
+ setDraft((d) => {
159
+ if (!d) return null;
160
+ const keep = d.type === "pen" ? d.points.length > 1 : d.type === "text" ? false : Math.abs(d.x1 - d.x0) > 5 || Math.abs(d.y1 - d.y0) > 5;
161
+ if (keep) setShapes((s) => [...s, d]);
162
+ return null;
163
+ });
164
+ startRef.current = null;
165
+ }, []);
166
+ const pan = React2.useRef(
167
+ reactNative.PanResponder.create({
168
+ onStartShouldSetPanResponder: () => true,
169
+ onMoveShouldSetPanResponder: () => true,
170
+ onPanResponderGrant: (e) => startDraw(e.nativeEvent.locationX, e.nativeEvent.locationY),
171
+ onPanResponderMove: (e) => moveDraw(e.nativeEvent.locationX, e.nativeEvent.locationY),
172
+ onPanResponderRelease: endDraw,
173
+ onPanResponderTerminate: endDraw
174
+ })
175
+ ).current;
176
+ const undo = () => {
177
+ setShapes((s) => s.slice(0, -1));
178
+ setDraft(null);
179
+ };
180
+ const clearAll = () => {
181
+ setShapes([]);
182
+ setDraft(null);
183
+ setEditingId(null);
184
+ };
185
+ const updateText = (id, val) => setShapes((s) => s.map((sh) => sh.id === id && sh.type === "text" ? { ...sh, text: val } : sh));
186
+ const commitText = () => setShapes((s) => {
187
+ setEditingId(null);
188
+ return s.filter((sh) => !(sh.type === "text" && !sh.text.trim()));
189
+ });
190
+ const handleContinue = async () => {
191
+ commitText();
192
+ const count = shapes.filter((sh) => !(sh.type === "text" && !sh.text.trim())).length;
193
+ try {
194
+ const uri = await reactNativeViewShot.captureRef(stageContainerRef, { format: "png", result: "data-uri", quality: 1 });
195
+ onContinue(typeof uri === "string" ? uri : src, count);
196
+ } catch {
197
+ onContinue(src, count);
198
+ }
199
+ };
200
+ const all = draft ? [...shapes, draft] : shapes;
201
+ const editingShape = editingId != null ? shapes.find((s) => s.id === editingId) : void 0;
202
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, animationType: "fade", transparent: true, statusBarTranslucent: true, onRequestClose: onCancel, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.screen, children: [
203
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.header, { paddingTop: insets.top + 8, height: HEADER_H + insets.top }], children: [
204
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onCancel, style: styles2.hdrGhost, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.hdrGhostText, children: "Back" }) }),
205
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.hdrTitle, children: "Annotate" }),
206
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
207
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: clearAll, style: styles2.hdrGhost, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.hdrGhostText, { color: "#FF6B6B" }], children: "Delete" }) }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: handleContinue, style: [styles2.hdrPrimary, { backgroundColor: t.accent }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.onAccent, fontWeight: "600", fontSize: 13.5 }, children: "Continue" }) })
209
+ ] })
210
+ ] }),
211
+ editingShape && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.editBar, { top: HEADER_H + insets.top + 6, backgroundColor: t.panel, borderColor: t.border }], children: [
212
+ /* @__PURE__ */ jsxRuntime.jsx(
213
+ reactNative.TextInput,
214
+ {
215
+ autoFocus: true,
216
+ value: editingShape.text,
217
+ placeholder: "Type a label, then Done",
218
+ placeholderTextColor: t.faint,
219
+ onChangeText: (v) => updateText(editingShape.id, v),
220
+ onSubmitEditing: commitText,
221
+ style: { flex: 1, color: t.text, fontSize: 15, paddingVertical: 6 }
222
+ },
223
+ editingShape.id
224
+ ),
225
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: commitText, style: [styles2.editDone, { backgroundColor: t.accent }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.onAccent, fontWeight: "600", fontSize: 13 }, children: "Done" }) })
226
+ ] }),
227
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.stageWrap, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: stageContainerRef, collapsable: false, style: [styles2.stage, { width: dims.width, height: dims.height }], children: [
228
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: src }, style: { width: dims.width, height: dims.height }, resizeMode: "contain" }),
229
+ all.map((sh) => {
230
+ if (sh.type !== "blur") return null;
231
+ return /* @__PURE__ */ jsxRuntime.jsx(
232
+ reactNative.View,
233
+ {
234
+ pointerEvents: "none",
235
+ style: {
236
+ position: "absolute",
237
+ left: Math.min(sh.x0, sh.x1),
238
+ top: Math.min(sh.y0, sh.y1),
239
+ width: Math.abs(sh.x1 - sh.x0),
240
+ height: Math.abs(sh.y1 - sh.y0),
241
+ backgroundColor: "#7A7D88",
242
+ borderWidth: 1.5,
243
+ borderColor: sh.color,
244
+ borderRadius: 5
245
+ }
246
+ },
247
+ sh.id
248
+ );
249
+ }),
250
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: dims.width, height: dims.height, style: reactNative.StyleSheet.absoluteFill, pointerEvents: "none", children: all.map((sh) => {
251
+ if (sh.type === "pen") {
252
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg3.Polyline, { points: pointsStr(sh.points), fill: "none", stroke: sh.color, strokeWidth: 3.5, strokeLinecap: "round", strokeLinejoin: "round" }, sh.id);
253
+ }
254
+ if (sh.type === "rect") {
255
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg3.Rect, { x: Math.min(sh.x0, sh.x1), y: Math.min(sh.y0, sh.y1), width: Math.abs(sh.x1 - sh.x0), height: Math.abs(sh.y1 - sh.y0), rx: 4, fill: "none", stroke: sh.color, strokeWidth: 3 }, sh.id);
256
+ }
257
+ if (sh.type === "arrow") {
258
+ const { x0, y0, x1, y1 } = sh;
259
+ const ang = Math.atan2(y1 - y0, x1 - x0);
260
+ const hl = 15;
261
+ return /* @__PURE__ */ jsxRuntime.jsxs(Svg3.G, { children: [
262
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3.Line, { x1: x0, y1: y0, x2: x1, y2: y1, stroke: sh.color, strokeWidth: 3.5, strokeLinecap: "round" }),
263
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3.Polyline, { points: `${x1 - hl * Math.cos(ang - Math.PI / 7)},${y1 - hl * Math.sin(ang - Math.PI / 7)} ${x1},${y1} ${x1 - hl * Math.cos(ang + Math.PI / 7)},${y1 - hl * Math.sin(ang + Math.PI / 7)}`, fill: "none", stroke: sh.color, strokeWidth: 3.5, strokeLinecap: "round", strokeLinejoin: "round" })
264
+ ] }, sh.id);
265
+ }
266
+ return null;
267
+ }) }),
268
+ all.map((sh) => {
269
+ if (sh.type !== "text" || !sh.text.trim()) return null;
270
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { pointerEvents: "none", style: { position: "absolute", left: sh.x, top: sh.y - 13, backgroundColor: sh.color, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 6 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: "#fff", fontSize: 13, fontWeight: "600" }, children: sh.text }) }, sh.id);
271
+ }),
272
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: reactNative.StyleSheet.absoluteFill, ...pan.panHandlers })
273
+ ] }) }),
274
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.toolbarWrap, { bottom: insets.bottom + 18 }], children: /* @__PURE__ */ jsxRuntime.jsx(AnnotateToolbar, { t, tool, color, onTool: setTool, onColor: setColor, onUndo: undo }) })
275
+ ] }) });
276
+ }
277
+ var styles2 = reactNative.StyleSheet.create({
278
+ screen: { flex: 1, backgroundColor: "rgba(8,10,18,0.94)" },
279
+ header: { position: "absolute", top: 0, left: 0, right: 0, flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 14, zIndex: 6 },
280
+ hdrTitle: { color: "#fff", fontSize: 15, fontWeight: "600" },
281
+ hdrGhost: { paddingHorizontal: 10, paddingVertical: 8 },
282
+ hdrGhostText: { color: "#fff", fontSize: 14, fontWeight: "500" },
283
+ hdrPrimary: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 10 },
284
+ editBar: { position: "absolute", left: 16, right: 16, flexDirection: "row", alignItems: "center", gap: 8, paddingLeft: 12, paddingRight: 6, paddingVertical: 4, borderRadius: 12, borderWidth: 1, zIndex: 7 },
285
+ editDone: { paddingHorizontal: 14, paddingVertical: 7, borderRadius: 9 },
286
+ stageWrap: { flex: 1, alignItems: "center", justifyContent: "center" },
287
+ stage: { borderRadius: 10, overflow: "hidden", backgroundColor: "#000" },
288
+ toolbarWrap: { position: "absolute", left: 16, right: 16, alignItems: "center" }
289
+ });
290
+ var BugReporterCaptureRefContext = React2.createContext(null);
291
+ var CurrentRefContext = React2.createContext(null);
292
+ function BugReporterCaptureRefProvider({ children }) {
293
+ const [currentRef, setCurrentRef] = React2.useState(null);
294
+ const setRef = React2.useCallback((ref) => {
295
+ setCurrentRef(() => ref);
296
+ }, []);
297
+ return /* @__PURE__ */ jsxRuntime.jsx(BugReporterCaptureRefContext.Provider, { value: setRef, children: /* @__PURE__ */ jsxRuntime.jsx(CurrentRefContext.Provider, { value: currentRef, children }) });
298
+ }
299
+ function useSetBugReporterCaptureRef(ref) {
300
+ const setRef = React2.useContext(BugReporterCaptureRefContext);
301
+ React2__default.default.useEffect(() => {
302
+ setRef?.(ref);
303
+ return () => setRef?.(null);
304
+ }, [setRef, ref]);
305
+ }
306
+ function useBugReporterCaptureRef() {
307
+ return React2.useContext(CurrentRefContext);
308
+ }
309
+ function BugReporterScreenCaptureView({
310
+ children,
311
+ style
312
+ }) {
313
+ const ref = React2.useRef(null);
314
+ useSetBugReporterCaptureRef(ref);
315
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ref, collapsable: false, style: [{ flex: 1 }, style], children });
316
+ }
317
+ function BugReportForm(props) {
318
+ const { t } = props;
319
+ const [metaOpen, setMetaOpen] = React2.useState(true);
320
+ const canSubmit = props.title.trim().length > 0 && !props.submitting;
321
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.root, { backgroundColor: t.panel }], children: [
322
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.header, { borderBottomColor: t.border }], children: [
323
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: props.onBack, style: [styles3.hdrBtn, { borderColor: t.border, backgroundColor: t.surface }], children: /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M15 5l-7 7 7 7", stroke: t.muted, strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
324
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
325
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.title, { color: t.text }], children: "New bug report" }),
326
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.subtitle, { color: t.muted }], children: "Review & describe the issue" })
327
+ ] }),
328
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: props.onClose, style: [styles3.hdrBtn, { borderColor: t.border, backgroundColor: t.surface }], children: /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M6 6l12 12M18 6L6 18", stroke: t.muted, strokeWidth: 2.2, strokeLinecap: "round" }) }) })
329
+ ] }),
330
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.ScrollView, { style: { flex: 1 }, contentContainerStyle: { padding: 16, gap: 18 }, keyboardShouldPersistTaps: "handled", children: [
331
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.shotRow, { borderColor: t.border, backgroundColor: t.surface }], children: [
332
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: 54, height: 38, borderRadius: 6, overflow: "hidden", backgroundColor: t.canvas, borderWidth: 1, borderColor: t.border }, children: props.screenshotUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: props.screenshotUrl }, style: { width: "100%", height: "100%" }, resizeMode: "cover" }) : null }),
333
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
334
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 13, fontWeight: "500", color: t.text }, children: "Screenshot attached" }),
335
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { fontSize: 12, color: t.muted, marginTop: 1 }, children: [
336
+ props.annotationCount,
337
+ " annotation",
338
+ props.annotationCount === 1 ? "" : "s"
339
+ ] })
340
+ ] }),
341
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: props.onEditAnnotations, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: t.accent }, children: "Edit" }) })
342
+ ] }),
343
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: 7 }, children: [
344
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, { color: t.muted }], children: "Summary" }),
345
+ /* @__PURE__ */ jsxRuntime.jsx(
346
+ reactNative.TextInput,
347
+ {
348
+ value: props.title,
349
+ onChangeText: props.onTitle,
350
+ placeholder: "Chart tooltip overflows on hover",
351
+ placeholderTextColor: t.faint,
352
+ style: [styles3.input, { color: t.text, backgroundColor: t.surface, borderColor: t.border }]
353
+ }
354
+ )
355
+ ] }),
356
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: 7 }, children: [
357
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, { color: t.muted }], children: "What happened?" }),
358
+ /* @__PURE__ */ jsxRuntime.jsx(
359
+ reactNative.TextInput,
360
+ {
361
+ value: props.description,
362
+ onChangeText: props.onDescription,
363
+ placeholder: "Steps to reproduce, expected vs actual\u2026",
364
+ placeholderTextColor: t.faint,
365
+ multiline: true,
366
+ style: [styles3.input, styles3.textArea, { color: t.text, backgroundColor: t.surface, borderColor: t.border }]
367
+ }
368
+ )
369
+ ] }),
370
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: 8 }, children: [
371
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, { color: t.muted }], children: "Severity" }),
372
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", gap: 6 }, children: bugReporterCore.SEVERITIES.map((s) => {
373
+ const active = props.severity === s.id;
374
+ return /* @__PURE__ */ jsxRuntime.jsxs(
375
+ reactNative.TouchableOpacity,
376
+ {
377
+ onPress: () => props.onSeverity(s.id),
378
+ style: [styles3.sev, { borderColor: active ? t.accent : t.border, backgroundColor: active ? t.ring : t.surface }],
379
+ children: [
380
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: 9, height: 9, borderRadius: 5, backgroundColor: s.color, opacity: active ? 1 : 0.55 } }),
381
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 12, fontWeight: active ? "600" : "500", color: active ? t.text : t.muted }, children: s.label })
382
+ ]
383
+ },
384
+ s.id
385
+ );
386
+ }) })
387
+ ] }),
388
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: 8 }, children: [
389
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, { color: t.muted }], children: "Type" }),
390
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 6 }, children: bugReporterCore.REPORT_TYPES.map((rt) => {
391
+ const active = props.type === rt.id;
392
+ return /* @__PURE__ */ jsxRuntime.jsx(
393
+ reactNative.TouchableOpacity,
394
+ {
395
+ onPress: () => props.onType(rt.id),
396
+ style: { paddingVertical: 7, paddingHorizontal: 13, borderRadius: 999, borderWidth: 1, borderColor: active ? t.accent : t.border, backgroundColor: active ? t.accent : t.surface },
397
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 12.5, fontWeight: active ? "600" : "500", color: active ? t.onAccent : t.muted }, children: rt.label })
398
+ },
399
+ rt.id
400
+ );
401
+ }) })
402
+ ] }),
403
+ props.meta.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { borderWidth: 1, borderColor: t.border, borderRadius: 10, overflow: "hidden", backgroundColor: t.surface }, children: [
404
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.TouchableOpacity, { onPress: () => setMetaOpen((o) => !o), style: { flexDirection: "row", alignItems: "center", gap: 8, padding: 11 }, children: [
405
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M4 7h16M4 12h16M4 17h10", stroke: t.muted, strokeWidth: 2, strokeLinecap: "round" }) }),
406
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { flex: 1, fontSize: 12.5, fontWeight: "600", color: t.text }, children: "Auto-captured context" }),
407
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { fontSize: 11, color: t.muted }, children: [
408
+ props.meta.length,
409
+ " fields"
410
+ ] }),
411
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.muted, fontSize: 11 }, children: metaOpen ? "\u25B2" : "\u25BC" })
412
+ ] }),
413
+ metaOpen && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { borderTopWidth: 1, borderTopColor: t.border, paddingHorizontal: 12, paddingBottom: 8 }, children: props.meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center", gap: 10, paddingVertical: 6, borderBottomWidth: 1, borderBottomColor: t.border }, children: [
414
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.muted, width: 64, fontSize: 11.5 }, children: m.label }),
415
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: { flex: 1, textAlign: "right", fontSize: 11.5, color: m.flag === "err" ? "#E5484D" : m.flag === "warn" ? "#F5A524" : t.text }, children: m.value })
416
+ ] }, i)) })
417
+ ] })
418
+ ] }),
419
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.footer, { borderTopColor: t.border, backgroundColor: t.panel }], children: [
420
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
421
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M4 13l4 4L20 5", stroke: t.ok, strokeWidth: 2.4, strokeLinecap: "round", strokeLinejoin: "round" }) }),
422
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { flex: 1, fontSize: 12, color: t.muted }, children: [
423
+ "Filing to ",
424
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.text, fontWeight: "600" }, children: props.backendName })
425
+ ] }),
426
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center", gap: 5, backgroundColor: t.okBg, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 999 }, children: [
427
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: 6, height: 6, borderRadius: 3, backgroundColor: t.ok } }),
428
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 11, color: t.ok }, children: "connected" })
429
+ ] })
430
+ ] }),
431
+ props.error ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 12.5, color: "#E5484D", fontWeight: "500" }, children: props.error }) : null,
432
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { disabled: !canSubmit, onPress: props.onSubmit, style: [styles3.submit, { backgroundColor: t.accent, opacity: canSubmit ? 1 : 0.85 }], children: props.submitting ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: t.onAccent }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.onAccent, fontSize: 14, fontWeight: "600" }, children: "Submit report" }) })
433
+ ] })
434
+ ] });
435
+ }
436
+ var styles3 = reactNative.StyleSheet.create({
437
+ root: { flex: 1, overflow: "hidden" },
438
+ header: { flexDirection: "row", alignItems: "center", gap: 10, paddingHorizontal: 16, paddingTop: 16, paddingBottom: 14, borderBottomWidth: 1 },
439
+ hdrBtn: { width: 30, height: 30, borderRadius: 8, borderWidth: 1, alignItems: "center", justifyContent: "center" },
440
+ title: { fontSize: 15, fontWeight: "600" },
441
+ subtitle: { fontSize: 12, marginTop: 1 },
442
+ shotRow: { flexDirection: "row", alignItems: "center", gap: 12, padding: 10, borderWidth: 1, borderRadius: 10 },
443
+ label: { fontSize: 12, fontWeight: "600" },
444
+ input: { borderWidth: 1, borderRadius: 9, paddingHorizontal: 12, paddingVertical: 10, fontSize: 14 },
445
+ textArea: { minHeight: 78, textAlignVertical: "top" },
446
+ sev: { flex: 1, alignItems: "center", gap: 5, paddingVertical: 9, paddingHorizontal: 4, borderRadius: 9, borderWidth: 1 },
447
+ footer: { borderTopWidth: 1, paddingHorizontal: 16, paddingTop: 12, paddingBottom: 14, gap: 11 },
448
+ submit: { paddingVertical: 12, borderRadius: 10, alignItems: "center", justifyContent: "center" }
449
+ });
450
+ var CAPTURE_DELAY_MS = 400;
451
+ var CAPTURE_TIMEOUT_MS = 8e3;
452
+ function viewShotOptions(result, options) {
453
+ const format = options?.format === "jpeg" ? "jpg" : "png";
454
+ const quality = options?.quality ?? (format === "jpg" ? 0.92 : 1);
455
+ return { format, quality, result };
456
+ }
457
+ function waitForLayout() {
458
+ return new Promise((resolve) => {
459
+ reactNative.InteractionManager.runAfterInteractions(() => {
460
+ setTimeout(resolve, CAPTURE_DELAY_MS);
461
+ });
462
+ });
463
+ }
464
+ function doCapture(viewRef, options) {
465
+ const view = viewRef?.current;
466
+ if (!view) return Promise.resolve(void 0);
467
+ return reactNativeViewShot.captureRef(viewRef, viewShotOptions("data-uri", options)).then((uri) => typeof uri === "string" ? uri : void 0).catch(() => void 0);
468
+ }
469
+ async function captureViewBase64(viewRef, options) {
470
+ await waitForLayout();
471
+ if (!viewRef) return void 0;
472
+ try {
473
+ const base64 = await reactNativeViewShot.captureRef(viewRef, viewShotOptions("base64", options));
474
+ return typeof base64 === "string" ? base64 : void 0;
475
+ } catch {
476
+ return void 0;
477
+ }
478
+ }
479
+ function withTimeout(p, ms) {
480
+ return Promise.race([
481
+ p,
482
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Capture timeout")), ms))
483
+ ]);
484
+ }
485
+ function imageUriFromCapture(uri) {
486
+ if (uri.startsWith("data:")) return uri;
487
+ if (uri.startsWith("file://")) return uri;
488
+ if (uri.startsWith("/")) {
489
+ return reactNative.Platform.OS === "android" ? "file://" + uri : uri;
490
+ }
491
+ return uri;
492
+ }
493
+ async function captureScreenDataUri(options) {
494
+ await waitForLayout();
495
+ for (const resultType of ["data-uri", "tmpfile"]) {
496
+ try {
497
+ const uri = await withTimeout(
498
+ reactNativeViewShot.captureScreen(viewShotOptions(resultType, options)),
499
+ CAPTURE_TIMEOUT_MS
500
+ );
501
+ if (typeof uri === "string" && uri.length > 0) {
502
+ return imageUriFromCapture(uri);
503
+ }
504
+ } catch {
505
+ }
506
+ }
507
+ return void 0;
508
+ }
509
+ async function captureViewDataUri(viewRef, options) {
510
+ try {
511
+ let dataUri = await captureScreenDataUri(options);
512
+ if (!dataUri && viewRef?.current) {
513
+ dataUri = await doCapture(viewRef, options);
514
+ if (!dataUri) {
515
+ await new Promise((r) => setTimeout(r, 150));
516
+ dataUri = await doCapture(viewRef, options);
517
+ }
518
+ }
519
+ return dataUri;
520
+ } catch {
521
+ return void 0;
522
+ }
523
+ }
524
+ function dataUrlToBase64(dataUrl) {
525
+ return dataUrl.replace(/^data:image\/\w+;base64,/, "");
526
+ }
527
+ function SuccessCard({ t, ticket, ticketTitle, backendName, onDone }) {
528
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.backdrop, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.card, { backgroundColor: t.panel, borderColor: t.border }], children: [
529
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.check, { backgroundColor: t.okBg }], children: /* @__PURE__ */ jsxRuntime.jsx(Svg3__default.default, { width: 27, height: 27, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M4 13l5 5L20 6", stroke: t.ok, strokeWidth: 2.6, strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
530
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.title, { color: t.text }], children: "Report filed" }),
531
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles4.sub, { color: t.muted }], children: [
532
+ "Tracked in ",
533
+ backendName,
534
+ " automatically."
535
+ ] }),
536
+ ticket ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.ticket, { backgroundColor: t.surface, borderColor: t.border }], children: [
537
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.accent, fontWeight: "600", fontSize: 13 }, children: ticket }),
538
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.faint }, children: "\xB7" }),
539
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: { color: t.muted, fontSize: 13, flexShrink: 1 }, children: ticketTitle })
540
+ ] }) : null,
541
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", gap: 8, marginTop: ticket ? 0 : 16 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onDone, style: [styles4.btn, { borderWidth: 1, borderColor: t.border, backgroundColor: t.surface }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: t.text, fontSize: 13.5, fontWeight: "600" }, children: "Done" }) }) })
542
+ ] }) });
543
+ }
544
+ var styles4 = reactNative.StyleSheet.create({
545
+ backdrop: { flex: 1, alignItems: "center", justifyContent: "center", backgroundColor: "rgba(8,10,18,0.55)", padding: 24 },
546
+ card: { width: 312, maxWidth: "100%", borderWidth: 1, borderRadius: 18, padding: 26, alignItems: "center" },
547
+ check: { width: 54, height: 54, borderRadius: 27, alignItems: "center", justifyContent: "center", marginBottom: 15 },
548
+ title: { fontSize: 17, fontWeight: "600" },
549
+ sub: { fontSize: 13, marginTop: 5, textAlign: "center", lineHeight: 19 },
550
+ ticket: { flexDirection: "row", alignItems: "center", gap: 9, marginVertical: 16, paddingVertical: 11, paddingHorizontal: 11, borderWidth: 1, borderRadius: 10, alignSelf: "stretch", justifyContent: "center" },
551
+ btn: { flex: 1, paddingVertical: 11, borderRadius: 10, alignItems: "center" }
552
+ });
553
+ function buildMeta(config) {
554
+ const rows = [];
555
+ rows.push({ label: "Platform", value: `${reactNative.Platform.OS} ${String(reactNative.Platform.Version)}` });
556
+ rows.push({ label: "Release", value: config.appVersion ? `${config.appName}@${config.appVersion}` : config.appName });
557
+ const di = config.deviceInfo ?? {};
558
+ if (di.model) rows.push({ label: "Device", value: String(di.model) });
559
+ const win = reactNative.Dimensions.get("window");
560
+ const scr = reactNative.Dimensions.get("screen");
561
+ rows.push({ label: "Viewport", value: `${Math.round(win.width)} \xD7 ${Math.round(win.height)}` });
562
+ rows.push({ label: "Screen", value: `${Math.round(scr.width)} \xD7 ${Math.round(scr.height)} @${reactNative.PixelRatio.get()}x` });
563
+ rows.push({ label: "Font scale", value: String(reactNative.PixelRatio.getFontScale()) });
564
+ rows.push({ label: "Orientation", value: win.width > win.height ? "landscape" : "portrait" });
565
+ const scheme = reactNative.Appearance.getColorScheme();
566
+ if (scheme) rows.push({ label: "Scheme", value: scheme });
567
+ try {
568
+ const opts = Intl.DateTimeFormat().resolvedOptions();
569
+ if (opts.locale) rows.push({ label: "Language", value: opts.locale });
570
+ if (opts.timeZone) rows.push({ label: "Timezone", value: opts.timeZone });
571
+ } catch {
572
+ }
573
+ const diag = config.collectDiagnostics?.();
574
+ const crumbs = diag?.breadcrumbs ?? [];
575
+ const errs = crumbs.filter((b) => b.level === "error").length;
576
+ const warns = crumbs.filter((b) => b.level === "warn").length;
577
+ if (errs || warns) rows.push({ label: "Console", value: `${errs} error${errs === 1 ? "" : "s"} \xB7 ${warns} warning${warns === 1 ? "" : "s"}`, flag: errs ? "err" : "warn" });
578
+ if (diag?.lastFailedApiCall) rows.push({ label: "Network", value: "1 failed request", flag: "warn" });
579
+ return rows;
580
+ }
581
+ function BugReporterButton({ config, captureRef: captureRef3, label = "Report a bug" }) {
582
+ const theme = React2.useMemo(() => bugReporterCore.resolveTheme(config.theme), [config.theme]);
583
+ const backendName = config.backendName ?? "ClickUp";
584
+ const contextRef = useBugReporterCaptureRef();
585
+ const effectiveRef = captureRef3 ?? contextRef;
586
+ const [step, setStep] = React2.useState("idle");
587
+ const [flash, setFlash] = React2.useState(false);
588
+ const [screenshot, setScreenshot] = React2.useState();
589
+ const [edited, setEdited] = React2.useState();
590
+ const [annCount, setAnnCount] = React2.useState(0);
591
+ const [title, setTitle] = React2.useState("");
592
+ const [desc, setDesc] = React2.useState("");
593
+ const [severity, setSeverity] = React2.useState("medium");
594
+ const [type, setType] = React2.useState("bug");
595
+ const [ticket, setTicket] = React2.useState();
596
+ const [error, setError] = React2.useState();
597
+ const [meta, setMeta] = React2.useState([]);
598
+ const ping = React2.useRef(new reactNative.Animated.Value(0)).current;
599
+ React2.useEffect(() => {
600
+ const loop = reactNative.Animated.loop(
601
+ reactNative.Animated.timing(ping, { toValue: 1, duration: 1900, easing: reactNative.Easing.out(reactNative.Easing.ease), useNativeDriver: true })
602
+ );
603
+ loop.start();
604
+ return () => loop.stop();
605
+ }, [ping]);
606
+ const initial = reactNative.Dimensions.get("window");
607
+ const pan = React2.useRef(new reactNative.Animated.ValueXY({ x: initial.width - FAB_SIZE - 22, y: initial.height - FAB_SIZE - 120 })).current;
608
+ const dragging = React2.useRef(false);
609
+ const openRef = React2.useRef(() => {
610
+ });
611
+ const fabPan = React2.useRef(
612
+ reactNative.PanResponder.create({
613
+ onStartShouldSetPanResponder: () => true,
614
+ onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
615
+ onPanResponderGrant: () => {
616
+ dragging.current = false;
617
+ pan.extractOffset();
618
+ },
619
+ onPanResponderMove: (_e, g) => {
620
+ if (Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5) dragging.current = true;
621
+ pan.setValue({ x: g.dx, y: g.dy });
622
+ },
623
+ onPanResponderRelease: () => {
624
+ pan.flattenOffset();
625
+ if (!dragging.current) {
626
+ openRef.current();
627
+ return;
628
+ }
629
+ const win = reactNative.Dimensions.get("window");
630
+ const cx = pan.x._value;
631
+ const cy = pan.y._value;
632
+ const targetX = cx + FAB_SIZE / 2 < win.width / 2 ? 22 : win.width - FAB_SIZE - 22;
633
+ const targetY = Math.max(60, Math.min(cy, win.height - FAB_SIZE - 90));
634
+ reactNative.Animated.spring(pan, { toValue: { x: targetX, y: targetY }, useNativeDriver: false, friction: 7, tension: 40 }).start();
635
+ }
636
+ })
637
+ ).current;
638
+ React2.useEffect(() => {
639
+ void bugReporterCore.getReportQueue(config)?.flush(config);
640
+ }, []);
641
+ const queue = bugReporterCore.getReportQueue(config);
642
+ const reset = () => {
643
+ setStep("idle");
644
+ setFlash(false);
645
+ setScreenshot(void 0);
646
+ setEdited(void 0);
647
+ setAnnCount(0);
648
+ setTitle("");
649
+ setDesc("");
650
+ setSeverity("medium");
651
+ setType("bug");
652
+ setTicket(void 0);
653
+ setError(void 0);
654
+ };
655
+ const open = async () => {
656
+ if (step !== "idle") return;
657
+ setStep("capturing");
658
+ const shot = await captureViewDataUri(effectiveRef ?? void 0, config.screenshot);
659
+ setScreenshot(shot);
660
+ setFlash(true);
661
+ setTimeout(() => {
662
+ setFlash(false);
663
+ setStep("annotate");
664
+ }, 220);
665
+ };
666
+ openRef.current = open;
667
+ const onContinue = (flattened, count) => {
668
+ setEdited(flattened);
669
+ setAnnCount(count);
670
+ setMeta(buildMeta(config));
671
+ setStep("form");
672
+ if (config.collectContext) {
673
+ void (async () => {
674
+ try {
675
+ const extra = bugReporterCore.normalizeContext(await config.collectContext());
676
+ if (extra.length) setMeta((m) => [...m, ...extra]);
677
+ } catch {
678
+ }
679
+ })();
680
+ }
681
+ };
682
+ const submit = async () => {
683
+ if (!title.trim()) return;
684
+ setStep("submitting");
685
+ setError(void 0);
686
+ const shot = edited ?? screenshot;
687
+ const options = {
688
+ title: title.trim(),
689
+ description: desc.trim(),
690
+ screenshotBase64: shot?.startsWith("data:") ? dataUrlToBase64(shot) : void 0,
691
+ isReportingProblem: true,
692
+ severity,
693
+ type,
694
+ context: meta.map((m) => ({ label: m.label, value: m.value })),
695
+ metadata: { platform: reactNative.Platform.OS, osVersion: reactNative.Platform.Version?.toString() }
696
+ };
697
+ try {
698
+ const res = await bugReporterCore.submitReport(config, options);
699
+ if (res.success) {
700
+ setTicket(res.id);
701
+ setStep("done");
702
+ } else if (queue) {
703
+ await queue.enqueue(options);
704
+ setStep("done");
705
+ } else {
706
+ setError(res.message || "Submission failed.");
707
+ setStep("form");
708
+ }
709
+ } catch (e) {
710
+ if (queue) {
711
+ await queue.enqueue(options);
712
+ setStep("done");
713
+ } else {
714
+ setError(e instanceof Error ? e.message : "Submission failed.");
715
+ setStep("form");
716
+ }
717
+ }
718
+ };
719
+ const pingStyle = {
720
+ transform: [{ scale: ping.interpolate({ inputRange: [0, 1], outputRange: [1, 1.9] }) }],
721
+ opacity: ping.interpolate({ inputRange: [0, 0.8, 1], outputRange: [0.55, 0, 0] })
722
+ };
723
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
724
+ step === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(
725
+ reactNative.Animated.View,
726
+ {
727
+ accessibilityRole: "button",
728
+ accessibilityLabel: label,
729
+ ...fabPan.panHandlers,
730
+ style: [styles5.fab, { backgroundColor: theme.accent, transform: pan.getTranslateTransform() }],
731
+ children: [
732
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [styles5.ping, { borderColor: theme.accent }, pingStyle], pointerEvents: "none" }),
733
+ /* @__PURE__ */ jsxRuntime.jsxs(Svg3__default.default, { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", children: [
734
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M8 11a4 4 0 018 0v3a4 4 0 01-8 0z", stroke: "#fff", strokeWidth: 1.7 }),
735
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3.Circle, { cx: 12, cy: 7.2, r: 2, stroke: "#fff", strokeWidth: 1.6 }),
736
+ /* @__PURE__ */ jsxRuntime.jsx(Svg3.Path, { d: "M10.4 5.6L9 4.2M13.6 5.6L15 4.2M8 12H5.4M8 15H5.6M16 12h2.6M16 15h2.4M8.4 17.6l-1.8 1.8M15.6 17.6l1.8 1.8", stroke: "#fff", strokeWidth: 1.5, strokeLinecap: "round" })
737
+ ] })
738
+ ]
739
+ }
740
+ ),
741
+ flash && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { transparent: true, animationType: "none", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.flash, pointerEvents: "none" }) }),
742
+ step === "annotate" && screenshot && /* @__PURE__ */ jsxRuntime.jsx(AnnotateOverlay, { t: theme, visible: true, src: screenshot, onCancel: reset, onContinue }),
743
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: step === "form" || step === "submitting", transparent: true, animationType: "slide", onRequestClose: reset, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.sheetBackdrop, children: [
744
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { style: reactNative.StyleSheet.absoluteFill, activeOpacity: 1, onPress: reset }),
745
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.sheet, children: /* @__PURE__ */ jsxRuntime.jsx(
746
+ BugReportForm,
747
+ {
748
+ t: theme,
749
+ screenshotUrl: edited ?? screenshot,
750
+ annotationCount: annCount,
751
+ title,
752
+ description: desc,
753
+ severity,
754
+ type,
755
+ meta,
756
+ backendName,
757
+ submitting: step === "submitting",
758
+ error,
759
+ onTitle: setTitle,
760
+ onDescription: setDesc,
761
+ onSeverity: setSeverity,
762
+ onType: setType,
763
+ onBack: () => setStep("annotate"),
764
+ onClose: reset,
765
+ onEditAnnotations: () => setStep("annotate"),
766
+ onSubmit: submit
767
+ }
768
+ ) })
769
+ ] }) }),
770
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: step === "done", transparent: true, animationType: "fade", onRequestClose: reset, children: /* @__PURE__ */ jsxRuntime.jsx(
771
+ SuccessCard,
772
+ {
773
+ t: theme,
774
+ ticket,
775
+ ticketTitle: title.trim() || "Untitled report",
776
+ backendName,
777
+ onDone: reset
778
+ }
779
+ ) })
780
+ ] });
781
+ }
782
+ var FAB_SIZE = 56;
783
+ var styles5 = reactNative.StyleSheet.create({
784
+ fab: { position: "absolute", top: 0, left: 0, width: FAB_SIZE, height: FAB_SIZE, borderRadius: FAB_SIZE / 2, alignItems: "center", justifyContent: "center", shadowColor: "#000", shadowOffset: { width: 0, height: 10 }, shadowOpacity: 0.34, shadowRadius: 18, elevation: 8, zIndex: 9999 },
785
+ ping: { position: "absolute", width: FAB_SIZE, height: FAB_SIZE, borderRadius: FAB_SIZE / 2, borderWidth: 2 },
786
+ flash: { flex: 1, backgroundColor: "#fff" },
787
+ sheetBackdrop: { flex: 1, justifyContent: "flex-end", backgroundColor: "rgba(8,10,18,0.5)" },
788
+ sheet: { height: "92%", borderTopLeftRadius: 26, borderTopRightRadius: 26, overflow: "hidden" }
789
+ });
790
+
791
+ exports.AnnotateOverlay = AnnotateOverlay;
792
+ exports.AnnotateToolbar = AnnotateToolbar;
793
+ exports.BugReportForm = BugReportForm;
794
+ exports.BugReporterButton = BugReporterButton;
795
+ exports.BugReporterCaptureRefProvider = BugReporterCaptureRefProvider;
796
+ exports.BugReporterScreenCaptureView = BugReporterScreenCaptureView;
797
+ exports.SuccessCard = SuccessCard;
798
+ exports.captureScreenDataUri = captureScreenDataUri;
799
+ exports.captureViewBase64 = captureViewBase64;
800
+ exports.captureViewDataUri = captureViewDataUri;
801
+ exports.dataUrlToBase64 = dataUrlToBase64;
802
+ exports.useBugReporterCaptureRef = useBugReporterCaptureRef;
803
+ exports.useSetBugReporterCaptureRef = useSetBugReporterCaptureRef;
804
+ //# sourceMappingURL=index.js.map
805
+ //# sourceMappingURL=index.js.map