@outcode/bug-reporter-web 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,1274 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var bugReporterCore = require('@outcode/bug-reporter-core');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var html2canvas = require('html2canvas');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
11
+
12
+ // src/BugReporterButton.tsx
13
+
14
+ // src/theme.ts
15
+ function cssVars(t) {
16
+ return {
17
+ "--canvas": t.canvas,
18
+ "--surface": t.surface,
19
+ "--panel": t.panel,
20
+ "--border": t.border,
21
+ "--border-strong": t.borderStrong,
22
+ "--text": t.text,
23
+ "--muted": t.muted,
24
+ "--faint": t.faint,
25
+ "--accent": t.accent,
26
+ "--accent-press": t.accentPress,
27
+ "--on-accent": t.onAccent,
28
+ "--ring": t.ring,
29
+ "--ok": t.ok,
30
+ "--ok-bg": t.okBg,
31
+ "--shadow": t.shadow
32
+ };
33
+ }
34
+ var FONT = "'Geist','Geist Fallback',-apple-system,system-ui,sans-serif";
35
+ var MONO = "'Geist Mono',ui-monospace,monospace";
36
+ var KEYFRAMES = `
37
+ @keyframes ocbr-spin{to{transform:rotate(360deg);}}
38
+ @keyframes ocbr-flash{0%{opacity:0;}18%{opacity:.92;}100%{opacity:0;}}
39
+ @keyframes ocbr-ping{0%{transform:scale(1);opacity:.55;}80%,100%{transform:scale(1.9);opacity:0;}}
40
+ @keyframes ocbr-float{0%,100%{transform:translateY(0);}50%{transform:translateY(-4px);}}
41
+ @keyframes ocbr-panel-in{from{transform:translateX(24px);opacity:0;}to{transform:translateX(0);opacity:1;}}
42
+ @keyframes ocbr-fade{from{opacity:0;}to{opacity:1;}}
43
+ @keyframes ocbr-pop{from{transform:scale(.92);opacity:0;}to{transform:scale(1);opacity:1;}}
44
+ `;
45
+ var injected = false;
46
+ function ensureKeyframes() {
47
+ if (injected || typeof document === "undefined") return;
48
+ injected = true;
49
+ const el = document.createElement("style");
50
+ el.setAttribute("data-ocbr", "");
51
+ el.textContent = KEYFRAMES;
52
+ document.head.appendChild(el);
53
+ }
54
+ function AnnotateToolbar({
55
+ tool,
56
+ color,
57
+ onTool,
58
+ onColor,
59
+ onUndo,
60
+ onClear,
61
+ onContinue
62
+ }) {
63
+ return /* @__PURE__ */ jsxRuntime.jsxs(
64
+ "div",
65
+ {
66
+ style: {
67
+ display: "flex",
68
+ flexWrap: "wrap",
69
+ alignItems: "center",
70
+ justifyContent: "center",
71
+ gap: 8,
72
+ padding: 7,
73
+ background: "var(--panel)",
74
+ border: "1px solid var(--border)",
75
+ borderRadius: 14,
76
+ boxShadow: "var(--shadow)",
77
+ fontFamily: FONT,
78
+ width: "100%",
79
+ boxSizing: "border-box"
80
+ },
81
+ children: [
82
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: 2 }, children: bugReporterCore.ANNOTATION_TOOLS.map((t) => {
83
+ const active = tool === t.id;
84
+ return /* @__PURE__ */ jsxRuntime.jsx(
85
+ "button",
86
+ {
87
+ type: "button",
88
+ title: t.label,
89
+ onClick: () => onTool(t.id),
90
+ style: {
91
+ width: 36,
92
+ height: 36,
93
+ display: "flex",
94
+ alignItems: "center",
95
+ justifyContent: "center",
96
+ border: "none",
97
+ borderRadius: 10,
98
+ background: active ? "var(--ring)" : "transparent",
99
+ color: active ? "var(--accent)" : "var(--muted)",
100
+ cursor: "pointer",
101
+ padding: 0
102
+ },
103
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
104
+ "path",
105
+ {
106
+ d: t.iconPath,
107
+ stroke: "currentColor",
108
+ strokeWidth: "2",
109
+ strokeLinecap: "round",
110
+ strokeLinejoin: "round"
111
+ }
112
+ ) })
113
+ },
114
+ t.id
115
+ );
116
+ }) }),
117
+ /* @__PURE__ */ jsxRuntime.jsx(
118
+ "div",
119
+ {
120
+ style: {
121
+ display: "flex",
122
+ alignItems: "center",
123
+ gap: 7,
124
+ padding: "0 4px",
125
+ borderLeft: "1px solid var(--border)",
126
+ borderRight: "1px solid var(--border)"
127
+ },
128
+ children: bugReporterCore.ANNOTATION_COLORS.map((c) => {
129
+ const active = color === c;
130
+ return /* @__PURE__ */ jsxRuntime.jsx(
131
+ "button",
132
+ {
133
+ type: "button",
134
+ "aria-label": "color",
135
+ onClick: () => onColor(c),
136
+ style: {
137
+ width: active ? 20 : 18,
138
+ height: active ? 20 : 18,
139
+ borderRadius: "50%",
140
+ border: "2px solid var(--panel)",
141
+ background: c,
142
+ cursor: "pointer",
143
+ padding: 0,
144
+ boxShadow: active ? "0 0 0 2px var(--accent)" : "0 0 0 1px var(--border)"
145
+ }
146
+ },
147
+ c
148
+ );
149
+ })
150
+ }
151
+ ),
152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 2 }, children: [
153
+ /* @__PURE__ */ jsxRuntime.jsx(
154
+ "button",
155
+ {
156
+ type: "button",
157
+ title: "Undo",
158
+ onClick: onUndo,
159
+ style: iconBtn,
160
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
161
+ "path",
162
+ {
163
+ d: "M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-3",
164
+ stroke: "currentColor",
165
+ strokeWidth: "2",
166
+ strokeLinecap: "round",
167
+ strokeLinejoin: "round"
168
+ }
169
+ ) })
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", title: "Clear all", onClick: onClear, style: iconBtn, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
173
+ "path",
174
+ {
175
+ d: "M5 7h14M9 7V5h6v2M7 7l1 12h8l1-12",
176
+ stroke: "currentColor",
177
+ strokeWidth: "2",
178
+ strokeLinecap: "round",
179
+ strokeLinejoin: "round"
180
+ }
181
+ ) }) })
182
+ ] }),
183
+ /* @__PURE__ */ jsxRuntime.jsxs(
184
+ "button",
185
+ {
186
+ type: "button",
187
+ onClick: onContinue,
188
+ style: {
189
+ display: "flex",
190
+ alignItems: "center",
191
+ gap: 7,
192
+ height: 36,
193
+ padding: "0 14px",
194
+ border: "none",
195
+ borderRadius: 10,
196
+ background: "var(--accent)",
197
+ color: "var(--on-accent)",
198
+ fontFamily: "inherit",
199
+ fontSize: 13.5,
200
+ fontWeight: 600,
201
+ cursor: "pointer"
202
+ },
203
+ children: [
204
+ "Continue",
205
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
206
+ "path",
207
+ {
208
+ d: "M5 12h14M13 6l6 6-6 6",
209
+ stroke: "currentColor",
210
+ strokeWidth: "2.2",
211
+ strokeLinecap: "round",
212
+ strokeLinejoin: "round"
213
+ }
214
+ ) })
215
+ ]
216
+ }
217
+ )
218
+ ]
219
+ }
220
+ );
221
+ }
222
+ var iconBtn = {
223
+ width: 36,
224
+ height: 36,
225
+ display: "flex",
226
+ alignItems: "center",
227
+ justifyContent: "center",
228
+ border: "none",
229
+ borderRadius: 10,
230
+ background: "transparent",
231
+ color: "var(--muted)",
232
+ cursor: "pointer",
233
+ padding: 0
234
+ };
235
+
236
+ // src/flatten.ts
237
+ function rectOf(b, sx, sy) {
238
+ const x = Math.min(b.x0, b.x1) * sx;
239
+ const y = Math.min(b.y0, b.y1) * sy;
240
+ const w = Math.abs(b.x1 - b.x0) * sx;
241
+ const h = Math.abs(b.y1 - b.y0) * sy;
242
+ return { x, y, w, h };
243
+ }
244
+ function flattenAnnotations(src, shapes, dispW, dispH) {
245
+ return new Promise((resolve) => {
246
+ if (typeof document === "undefined") return resolve(src);
247
+ const img = new Image();
248
+ img.onerror = () => resolve(src);
249
+ img.onload = () => {
250
+ const natW = img.naturalWidth || dispW;
251
+ const natH = img.naturalHeight || dispH;
252
+ const sx = dispW ? natW / dispW : 1;
253
+ const sy = dispH ? natH / dispH : 1;
254
+ const canvas = document.createElement("canvas");
255
+ canvas.width = natW;
256
+ canvas.height = natH;
257
+ const ctx = canvas.getContext("2d");
258
+ if (!ctx) return resolve(src);
259
+ ctx.drawImage(img, 0, 0, natW, natH);
260
+ shapes.forEach((s) => {
261
+ if (s.type !== "blur") return;
262
+ const { x, y, w, h } = rectOf(s, sx, sy);
263
+ ctx.fillStyle = "#7A7D88";
264
+ ctx.fillRect(x, y, w, h);
265
+ });
266
+ const lw = 3.5 * ((sx + sy) / 2);
267
+ ctx.lineCap = "round";
268
+ ctx.lineJoin = "round";
269
+ shapes.forEach((s) => {
270
+ ctx.strokeStyle = s.color;
271
+ ctx.fillStyle = s.color;
272
+ ctx.lineWidth = lw;
273
+ if (s.type === "pen") {
274
+ if (s.points.length < 2) return;
275
+ ctx.beginPath();
276
+ ctx.moveTo(s.points[0].x * sx, s.points[0].y * sy);
277
+ s.points.slice(1).forEach((p) => ctx.lineTo(p.x * sx, p.y * sy));
278
+ ctx.stroke();
279
+ } else if (s.type === "rect") {
280
+ const { x, y, w, h } = rectOf(s, sx, sy);
281
+ ctx.strokeRect(x, y, w, h);
282
+ } else if (s.type === "arrow") {
283
+ const x0 = s.x0 * sx;
284
+ const y0 = s.y0 * sy;
285
+ const x1 = s.x1 * sx;
286
+ const y1 = s.y1 * sy;
287
+ const ang = Math.atan2(y1 - y0, x1 - x0);
288
+ const hl = 15 * ((sx + sy) / 2);
289
+ ctx.beginPath();
290
+ ctx.moveTo(x0, y0);
291
+ ctx.lineTo(x1, y1);
292
+ ctx.stroke();
293
+ ctx.beginPath();
294
+ ctx.moveTo(x1 - hl * Math.cos(ang - Math.PI / 7), y1 - hl * Math.sin(ang - Math.PI / 7));
295
+ ctx.lineTo(x1, y1);
296
+ ctx.lineTo(x1 - hl * Math.cos(ang + Math.PI / 7), y1 - hl * Math.sin(ang + Math.PI / 7));
297
+ ctx.stroke();
298
+ } else if (s.type === "text" && s.text.trim()) {
299
+ const fs = 13 * sy;
300
+ ctx.font = `600 ${fs}px 'Geist',sans-serif`;
301
+ const padX = 8 * sx;
302
+ const padY = 4 * sy;
303
+ const tw = ctx.measureText(s.text).width;
304
+ const bx = s.x * sx;
305
+ const by = s.y * sy - (fs + padY * 2) / 2;
306
+ ctx.fillStyle = s.color;
307
+ const r = 6 * sy;
308
+ roundRect(ctx, bx, by, tw + padX * 2, fs + padY * 2, r);
309
+ ctx.fill();
310
+ ctx.fillStyle = "#ffffff";
311
+ ctx.textBaseline = "middle";
312
+ ctx.fillText(s.text, bx + padX, by + (fs + padY * 2) / 2);
313
+ }
314
+ });
315
+ resolve(canvas.toDataURL("image/png"));
316
+ };
317
+ img.src = src;
318
+ });
319
+ }
320
+ function roundRect(ctx, x, y, w, h, r) {
321
+ const rad = Math.min(r, w / 2, h / 2);
322
+ ctx.beginPath();
323
+ ctx.moveTo(x + rad, y);
324
+ ctx.arcTo(x + w, y, x + w, y + h, rad);
325
+ ctx.arcTo(x + w, y + h, x, y + h, rad);
326
+ ctx.arcTo(x, y + h, x, y, rad);
327
+ ctx.arcTo(x, y, x + w, y, rad);
328
+ ctx.closePath();
329
+ }
330
+ var PADDING = 24;
331
+ var TOP_RESERVE = 150;
332
+ var BOTTOM_RESERVE = 40;
333
+ function AnnotateOverlay({ src, onCancel, onContinue }) {
334
+ const stageRef = react.useRef(null);
335
+ const idRef = react.useRef(0);
336
+ const [shapes, setShapes] = react.useState([]);
337
+ const [draft, setDraft] = react.useState(null);
338
+ const [tool, setTool] = react.useState("arrow");
339
+ const [color, setColor] = react.useState(bugReporterCore.ANNOTATION_COLORS[0]);
340
+ const [editingId, setEditingId] = react.useState(null);
341
+ const [dims, setDims] = react.useState({ width: 800, height: 480 });
342
+ const uid = () => `s${++idRef.current}`;
343
+ const recompute = react.useCallback((natW, natH) => {
344
+ const maxW = window.innerWidth - PADDING * 2;
345
+ const maxH = window.innerHeight - TOP_RESERVE - BOTTOM_RESERVE - PADDING * 2;
346
+ const aspect = natW / natH || 1.6;
347
+ let w = maxW;
348
+ let h = Math.round(maxW / aspect);
349
+ if (h > maxH) {
350
+ h = maxH;
351
+ w = Math.round(maxH * aspect);
352
+ }
353
+ setDims({ width: w, height: h });
354
+ }, []);
355
+ react.useEffect(() => {
356
+ const img = new Image();
357
+ img.onload = () => recompute(img.naturalWidth, img.naturalHeight);
358
+ img.onerror = () => recompute(1440, 900);
359
+ img.src = src;
360
+ }, [src, recompute]);
361
+ const getPt = (e) => {
362
+ const r = stageRef.current.getBoundingClientRect();
363
+ return {
364
+ x: Math.max(0, Math.min(r.width, e.clientX - r.left)),
365
+ y: Math.max(0, Math.min(r.height, e.clientY - r.top))
366
+ };
367
+ };
368
+ const onDown = (e) => {
369
+ if (!stageRef.current) return;
370
+ const p = getPt(e);
371
+ if (tool === "text") {
372
+ for (let i = shapes.length - 1; i >= 0; i--) {
373
+ const sh = shapes[i];
374
+ if (sh.type !== "text") continue;
375
+ const w = Math.max(48, sh.text.length * 8 + 24);
376
+ if (p.x >= sh.x - 8 && p.x <= sh.x + w && p.y >= sh.y - 20 && p.y <= sh.y + 10) {
377
+ setEditingId(sh.id);
378
+ return;
379
+ }
380
+ }
381
+ const id = uid();
382
+ setShapes((s) => [...s, { id, type: "text", x: p.x, y: p.y, color, text: "" }]);
383
+ setEditingId(id);
384
+ return;
385
+ }
386
+ try {
387
+ e.currentTarget.setPointerCapture(e.pointerId);
388
+ } catch {
389
+ }
390
+ const d = tool === "pen" ? { id: uid(), type: "pen", color, points: [p] } : { id: uid(), type: tool, color, x0: p.x, y0: p.y, x1: p.x, y1: p.y };
391
+ setDraft(d);
392
+ setEditingId(null);
393
+ };
394
+ const onMove = (e) => {
395
+ setDraft((prev) => {
396
+ if (!prev) return prev;
397
+ const p = getPt(e);
398
+ if (prev.type === "pen") return { ...prev, points: [...prev.points, p] };
399
+ return { ...prev, x1: p.x, y1: p.y };
400
+ });
401
+ };
402
+ const onUp = () => {
403
+ setDraft((d) => {
404
+ if (!d) return null;
405
+ 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;
406
+ if (keep) setShapes((s) => [...s, d]);
407
+ return null;
408
+ });
409
+ };
410
+ const undo = () => {
411
+ setShapes((s) => s.slice(0, -1));
412
+ setDraft(null);
413
+ };
414
+ const clearAll = () => {
415
+ setShapes([]);
416
+ setDraft(null);
417
+ setEditingId(null);
418
+ };
419
+ const updateText = (id, val) => setShapes((s) => s.map((sh) => sh.id === id && sh.type === "text" ? { ...sh, text: val } : sh));
420
+ const commitText = () => setShapes((s) => {
421
+ setEditingId(null);
422
+ return s.filter((sh) => !(sh.type === "text" && !sh.text.trim()));
423
+ });
424
+ const handleContinue = async () => {
425
+ const flattened = await flattenAnnotations(src, shapes, dims.width, dims.height);
426
+ onContinue(flattened, shapes.length);
427
+ };
428
+ const all = draft ? [...shapes, draft] : shapes;
429
+ const svgEls = [];
430
+ const blurEls = [];
431
+ const labelEls = [];
432
+ all.forEach((sh) => {
433
+ if (sh.type === "blur") {
434
+ const x = Math.min(sh.x0, sh.x1);
435
+ const y = Math.min(sh.y0, sh.y1);
436
+ const w = Math.abs(sh.x1 - sh.x0);
437
+ const h = Math.abs(sh.y1 - sh.y0);
438
+ blurEls.push(
439
+ /* @__PURE__ */ jsxRuntime.jsx(
440
+ "div",
441
+ {
442
+ style: {
443
+ position: "absolute",
444
+ left: x,
445
+ top: y,
446
+ width: w,
447
+ height: h,
448
+ backdropFilter: "blur(10px)",
449
+ WebkitBackdropFilter: "blur(10px)",
450
+ background: "#7A7D88",
451
+ border: `1.5px dashed ${sh.color}`,
452
+ borderRadius: 5
453
+ }
454
+ },
455
+ sh.id
456
+ )
457
+ );
458
+ } else if (sh.type === "rect") {
459
+ const x = Math.min(sh.x0, sh.x1);
460
+ const y = Math.min(sh.y0, sh.y1);
461
+ svgEls.push(
462
+ /* @__PURE__ */ jsxRuntime.jsx(
463
+ "rect",
464
+ {
465
+ x,
466
+ y,
467
+ width: Math.abs(sh.x1 - sh.x0),
468
+ height: Math.abs(sh.y1 - sh.y0),
469
+ rx: 4,
470
+ fill: "none",
471
+ stroke: sh.color,
472
+ strokeWidth: 3
473
+ },
474
+ sh.id
475
+ )
476
+ );
477
+ } else if (sh.type === "pen") {
478
+ svgEls.push(
479
+ /* @__PURE__ */ jsxRuntime.jsx(
480
+ "polyline",
481
+ {
482
+ points: sh.points.map((p) => `${p.x},${p.y}`).join(" "),
483
+ fill: "none",
484
+ stroke: sh.color,
485
+ strokeWidth: 3.5,
486
+ strokeLinecap: "round",
487
+ strokeLinejoin: "round"
488
+ },
489
+ sh.id
490
+ )
491
+ );
492
+ } else if (sh.type === "arrow") {
493
+ const { x0, y0, x1, y1 } = sh;
494
+ const ang = Math.atan2(y1 - y0, x1 - x0);
495
+ const hl = 15;
496
+ svgEls.push(
497
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
498
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x0, y1: y0, x2: x1, y2: y1, stroke: sh.color, strokeWidth: 3.5, strokeLinecap: "round" }),
499
+ /* @__PURE__ */ jsxRuntime.jsx(
500
+ "polyline",
501
+ {
502
+ 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)}`,
503
+ fill: "none",
504
+ stroke: sh.color,
505
+ strokeWidth: 3.5,
506
+ strokeLinecap: "round",
507
+ strokeLinejoin: "round"
508
+ }
509
+ )
510
+ ] }, sh.id)
511
+ );
512
+ } else if (sh.type === "text") {
513
+ if (editingId === sh.id || !sh.text.trim()) return;
514
+ labelEls.push(
515
+ /* @__PURE__ */ jsxRuntime.jsx(
516
+ "div",
517
+ {
518
+ style: {
519
+ position: "absolute",
520
+ left: sh.x,
521
+ top: sh.y,
522
+ transform: "translateY(-50%)",
523
+ background: sh.color,
524
+ color: "#fff",
525
+ padding: "3px 8px",
526
+ borderRadius: 6,
527
+ fontSize: 13,
528
+ fontWeight: 600,
529
+ fontFamily: FONT,
530
+ whiteSpace: "nowrap",
531
+ boxShadow: "0 1px 4px rgba(0,0,0,0.28)"
532
+ },
533
+ children: sh.text
534
+ },
535
+ sh.id
536
+ )
537
+ );
538
+ }
539
+ });
540
+ const editingShape = editingId != null ? shapes.find((s) => s.id === editingId && s.type === "text") : void 0;
541
+ return /* @__PURE__ */ jsxRuntime.jsxs(
542
+ "div",
543
+ {
544
+ style: {
545
+ position: "fixed",
546
+ inset: 0,
547
+ zIndex: 2147483e3,
548
+ display: "flex",
549
+ flexDirection: "column",
550
+ alignItems: "center",
551
+ justifyContent: "center",
552
+ background: "rgba(8,10,18,0.92)",
553
+ fontFamily: FONT
554
+ },
555
+ children: [
556
+ /* @__PURE__ */ jsxRuntime.jsx(
557
+ "button",
558
+ {
559
+ type: "button",
560
+ "aria-label": "Cancel",
561
+ onClick: onCancel,
562
+ style: {
563
+ position: "absolute",
564
+ top: 18,
565
+ left: 18,
566
+ width: 36,
567
+ height: 36,
568
+ borderRadius: 9,
569
+ border: "1px solid rgba(255,255,255,0.18)",
570
+ background: "rgba(255,255,255,0.06)",
571
+ color: "#fff",
572
+ cursor: "pointer",
573
+ display: "flex",
574
+ alignItems: "center",
575
+ justifyContent: "center"
576
+ },
577
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }) })
578
+ }
579
+ ),
580
+ /* @__PURE__ */ jsxRuntime.jsx(
581
+ "div",
582
+ {
583
+ style: {
584
+ position: "absolute",
585
+ top: 24,
586
+ left: "50%",
587
+ transform: "translateX(-50%)",
588
+ background: "rgba(8,10,18,0.82)",
589
+ color: "#fff",
590
+ fontSize: 12,
591
+ fontWeight: 550,
592
+ padding: "6px 13px",
593
+ borderRadius: 999,
594
+ backdropFilter: "blur(6px)"
595
+ },
596
+ children: "Mark up the screenshot \u2014 draw, point, box, or blur sensitive data"
597
+ }
598
+ ),
599
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: 62, left: "50%", transform: "translateX(-50%)", width: 600, maxWidth: "92vw", zIndex: 5 }, children: /* @__PURE__ */ jsxRuntime.jsx(
600
+ AnnotateToolbar,
601
+ {
602
+ tool,
603
+ color,
604
+ onTool: setTool,
605
+ onColor: setColor,
606
+ onUndo: undo,
607
+ onClear: clearAll,
608
+ onContinue: handleContinue
609
+ }
610
+ ) }),
611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: dims.width, height: dims.height, marginTop: 70, borderRadius: 10, overflow: "hidden", boxShadow: "0 24px 70px rgba(0,0,0,0.5)" }, children: [
612
+ /* @__PURE__ */ jsxRuntime.jsx(
613
+ "img",
614
+ {
615
+ src,
616
+ alt: "Screenshot",
617
+ draggable: false,
618
+ style: { position: "absolute", inset: 0, width: dims.width, height: dims.height, objectFit: "contain", pointerEvents: "none" }
619
+ }
620
+ ),
621
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, pointerEvents: "none" }, children: blurEls }),
622
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "100%", height: "100%", style: { position: "absolute", inset: 0, overflow: "visible", pointerEvents: "none" }, children: svgEls }),
623
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, pointerEvents: "none" }, children: labelEls }),
624
+ editingShape && editingShape.type === "text" && /* @__PURE__ */ jsxRuntime.jsx(
625
+ "input",
626
+ {
627
+ ref: (el) => {
628
+ if (el && document.activeElement !== el) el.focus();
629
+ },
630
+ value: editingShape.text,
631
+ placeholder: "Label\u2026",
632
+ onChange: (e) => updateText(editingShape.id, e.target.value),
633
+ onKeyDown: (e) => {
634
+ if (e.key === "Enter" || e.key === "Escape") {
635
+ e.preventDefault();
636
+ commitText();
637
+ }
638
+ },
639
+ onBlur: commitText,
640
+ style: {
641
+ position: "absolute",
642
+ left: editingShape.x,
643
+ top: editingShape.y,
644
+ transform: "translateY(-50%)",
645
+ background: editingShape.color,
646
+ color: "#fff",
647
+ padding: "3px 8px",
648
+ borderRadius: 6,
649
+ fontSize: 13,
650
+ fontWeight: 600,
651
+ fontFamily: FONT,
652
+ border: "none",
653
+ outline: "none",
654
+ minWidth: 64,
655
+ boxShadow: "0 0 0 2px rgba(255,255,255,0.7),0 2px 10px rgba(0,0,0,0.3)",
656
+ zIndex: 30
657
+ }
658
+ }
659
+ ),
660
+ /* @__PURE__ */ jsxRuntime.jsx(
661
+ "div",
662
+ {
663
+ ref: stageRef,
664
+ onPointerDown: onDown,
665
+ onPointerMove: onMove,
666
+ onPointerUp: onUp,
667
+ onPointerCancel: onUp,
668
+ style: { position: "absolute", inset: 0, touchAction: "none", userSelect: "none", cursor: tool === "text" ? "text" : "crosshair" }
669
+ }
670
+ )
671
+ ] })
672
+ ]
673
+ }
674
+ );
675
+ }
676
+ function BugReportForm(props) {
677
+ const [metaOpen, setMetaOpen] = react.useState(true);
678
+ const {
679
+ screenshotUrl,
680
+ annotationCount,
681
+ title,
682
+ description,
683
+ severity,
684
+ type,
685
+ meta,
686
+ backendName,
687
+ submitting,
688
+ error
689
+ } = props;
690
+ return /* @__PURE__ */ jsxRuntime.jsxs(
691
+ "div",
692
+ {
693
+ style: {
694
+ display: "flex",
695
+ flexDirection: "column",
696
+ height: "100%",
697
+ width: "100%",
698
+ background: "var(--panel)",
699
+ color: "var(--text)",
700
+ fontFamily: FONT,
701
+ overflow: "hidden",
702
+ boxSizing: "border-box"
703
+ },
704
+ children: [
705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "16px 16px 14px", borderBottom: "1px solid var(--border)", flexShrink: 0 }, children: [
706
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Back", onClick: props.onBack, style: hdrBtn, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5l-7 7 7 7", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
707
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
708
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em" }, children: "New bug report" }),
709
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "var(--muted)", marginTop: 1 }, children: "Review & describe the issue" })
710
+ ] }),
711
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Close", onClick: props.onClose, style: hdrBtn, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }) }) })
712
+ ] }),
713
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, overflowY: "auto", padding: 16, display: "flex", flexDirection: "column", gap: 18 }, children: [
714
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, padding: 10, border: "1px solid var(--border)", borderRadius: 10, background: "var(--surface)" }, children: [
715
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 54, height: 38, borderRadius: 6, background: "var(--canvas)", border: "1px solid var(--border)", overflow: "hidden", flexShrink: 0 }, children: screenshotUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: screenshotUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover" } }) }),
716
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
717
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 550 }, children: "Screenshot attached" }),
718
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "var(--muted)", marginTop: 1, fontFamily: MONO }, children: [
719
+ annotationCount,
720
+ " annotation",
721
+ annotationCount === 1 ? "" : "s"
722
+ ] })
723
+ ] }),
724
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onEditAnnotations, style: { fontSize: 12, fontWeight: 550, color: "var(--accent)", background: "none", border: "none", cursor: "pointer", padding: "6px 8px", borderRadius: 7 }, children: "Edit" })
725
+ ] }),
726
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Summary", children: /* @__PURE__ */ jsxRuntime.jsx(
727
+ "input",
728
+ {
729
+ value: title,
730
+ onChange: (e) => props.onTitle(e.target.value),
731
+ placeholder: "Chart tooltip overflows on hover",
732
+ style: inputStyle
733
+ }
734
+ ) }),
735
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: "What happened?", children: /* @__PURE__ */ jsxRuntime.jsx(
736
+ "textarea",
737
+ {
738
+ value: description,
739
+ onChange: (e) => props.onDescription(e.target.value),
740
+ placeholder: "Steps to reproduce, expected vs actual\u2026",
741
+ style: { ...inputStyle, resize: "none", minHeight: 78, lineHeight: 1.5 }
742
+ }
743
+ ) }),
744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
745
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Severity" }),
746
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 6 }, children: bugReporterCore.SEVERITIES.map((s) => {
747
+ const active = severity === s.id;
748
+ return /* @__PURE__ */ jsxRuntime.jsxs(
749
+ "button",
750
+ {
751
+ type: "button",
752
+ onClick: () => props.onSeverity(s.id),
753
+ style: {
754
+ display: "flex",
755
+ flexDirection: "column",
756
+ alignItems: "center",
757
+ gap: 5,
758
+ padding: "9px 4px",
759
+ borderRadius: 9,
760
+ border: `1px solid ${active ? "var(--accent)" : "var(--border)"}`,
761
+ background: active ? "var(--ring)" : "var(--surface)",
762
+ cursor: "pointer",
763
+ fontFamily: "inherit"
764
+ },
765
+ children: [
766
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 9, height: 9, borderRadius: "50%", background: s.color, opacity: active ? 1 : 0.55, boxShadow: active ? `0 0 0 3px color-mix(in srgb,${s.color} 22%,transparent)` : "none" } }),
767
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, fontWeight: active ? 600 : 550, color: active ? "var(--text)" : "var(--muted)" }, children: s.label })
768
+ ]
769
+ },
770
+ s.id
771
+ );
772
+ }) })
773
+ ] }),
774
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
775
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Type" }),
776
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: bugReporterCore.REPORT_TYPES.map((t) => {
777
+ const active = type === t.id;
778
+ return /* @__PURE__ */ jsxRuntime.jsx(
779
+ "button",
780
+ {
781
+ type: "button",
782
+ onClick: () => props.onType(t.id),
783
+ style: {
784
+ padding: "7px 13px",
785
+ borderRadius: 999,
786
+ border: `1px solid ${active ? "var(--accent)" : "var(--border)"}`,
787
+ background: active ? "var(--accent)" : "var(--surface)",
788
+ color: active ? "var(--on-accent)" : "var(--muted)",
789
+ fontSize: 12.5,
790
+ fontWeight: active ? 600 : 550,
791
+ cursor: "pointer",
792
+ fontFamily: "inherit"
793
+ },
794
+ children: t.label
795
+ },
796
+ t.id
797
+ );
798
+ }) })
799
+ ] }),
800
+ meta.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid var(--border)", borderRadius: 10, overflow: "hidden", background: "var(--surface)" }, children: [
801
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => setMetaOpen((o) => !o), style: { width: "100%", display: "flex", alignItems: "center", gap: 8, padding: "11px 12px", background: "none", border: "none", cursor: "pointer", fontFamily: "inherit", color: "var(--text)" }, children: [
802
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", style: { color: "var(--muted)" }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 7h16M4 12h16M4 17h10", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
803
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, textAlign: "left", fontSize: 12.5, fontWeight: 600 }, children: "Auto-captured context" }),
804
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 11, color: "var(--muted)", fontFamily: MONO }, children: [
805
+ meta.length,
806
+ " fields"
807
+ ] }),
808
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", style: { color: "var(--muted)", transform: metaOpen ? "rotate(180deg)" : "rotate(0deg)", transition: "transform .18s" }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
809
+ ] }),
810
+ metaOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderTop: "1px solid var(--border)", padding: "4px 12px 8px" }, children: meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "6px 0", borderBottom: "1px solid var(--border)", fontFamily: MONO, fontSize: 11.5 }, children: [
811
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--muted)", width: 62, flexShrink: 0 }, children: m.label }),
812
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, color: m.flag === "err" ? "#E5484D" : m.flag === "warn" ? "#F5A524" : "var(--text)", textAlign: "right", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.value })
813
+ ] }, i)) })
814
+ ] })
815
+ ] }),
816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flexShrink: 0, borderTop: "1px solid var(--border)", padding: "12px 16px 14px", display: "flex", flexDirection: "column", gap: 11, background: "var(--panel)" }, children: [
817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12, color: "var(--muted)" }, children: [
818
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 13l4 4L20 5", stroke: "var(--ok)", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round" }) }),
819
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1 }, children: [
820
+ "Filing to ",
821
+ /* @__PURE__ */ jsxRuntime.jsx("b", { style: { color: "var(--text)", fontWeight: 600 }, children: backendName })
822
+ ] }),
823
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 5, fontFamily: MONO, fontSize: 11, color: "var(--ok)", background: "var(--ok-bg)", padding: "3px 8px", borderRadius: 999 }, children: [
824
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: "var(--ok)" } }),
825
+ "connected"
826
+ ] })
827
+ ] }),
828
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12.5, color: "#E5484D", fontWeight: 550 }, children: error }),
829
+ /* @__PURE__ */ jsxRuntime.jsxs(
830
+ "button",
831
+ {
832
+ type: "button",
833
+ disabled: submitting || !title.trim(),
834
+ onClick: props.onSubmit,
835
+ style: {
836
+ width: "100%",
837
+ display: "flex",
838
+ alignItems: "center",
839
+ justifyContent: "center",
840
+ gap: 9,
841
+ padding: 12,
842
+ borderRadius: 10,
843
+ border: "none",
844
+ background: "var(--accent)",
845
+ color: "var(--on-accent)",
846
+ fontSize: 14,
847
+ fontWeight: 600,
848
+ fontFamily: "inherit",
849
+ cursor: submitting || !title.trim() ? "default" : "pointer",
850
+ opacity: submitting || !title.trim() ? 0.85 : 1
851
+ },
852
+ children: [
853
+ submitting && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 15, height: 15, borderRadius: "50%", border: "2px solid var(--on-accent)", borderTopColor: "transparent", animation: "ocbr-spin .7s linear infinite", display: "inline-block" } }),
854
+ submitting ? "Filing report\u2026" : "Submit report"
855
+ ]
856
+ }
857
+ )
858
+ ] })
859
+ ]
860
+ }
861
+ );
862
+ }
863
+ function Field({ label, children }) {
864
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 7 }, children: [
865
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: label }),
866
+ children
867
+ ] });
868
+ }
869
+ function Label({ children }) {
870
+ return /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 12, fontWeight: 600, color: "var(--muted)", letterSpacing: "0.01em" }, children });
871
+ }
872
+ var hdrBtn = {
873
+ width: 30,
874
+ height: 30,
875
+ display: "flex",
876
+ alignItems: "center",
877
+ justifyContent: "center",
878
+ border: "1px solid var(--border)",
879
+ background: "var(--surface)",
880
+ borderRadius: 8,
881
+ cursor: "pointer",
882
+ color: "var(--muted)",
883
+ padding: 0
884
+ };
885
+ var inputStyle = {
886
+ width: "100%",
887
+ boxSizing: "border-box",
888
+ padding: "10px 12px",
889
+ fontSize: 14,
890
+ fontFamily: "inherit",
891
+ color: "var(--text)",
892
+ background: "var(--surface)",
893
+ border: "1px solid var(--border)",
894
+ borderRadius: 9,
895
+ outline: "none"
896
+ };
897
+ var DEFAULTS = {
898
+ maxWidth: 1280,
899
+ format: "png",
900
+ quality: 0.92
901
+ };
902
+ function downscale(source, maxWidth) {
903
+ if (source.width <= maxWidth) return source;
904
+ const scale = maxWidth / source.width;
905
+ const target = document.createElement("canvas");
906
+ target.width = Math.round(source.width * scale);
907
+ target.height = Math.round(source.height * scale);
908
+ const ctx = target.getContext("2d");
909
+ if (!ctx) return source;
910
+ ctx.drawImage(source, 0, 0, target.width, target.height);
911
+ return target;
912
+ }
913
+ async function captureViewport(options) {
914
+ if (typeof document === "undefined" || typeof window === "undefined") return void 0;
915
+ const { maxWidth, format, quality } = { ...DEFAULTS, ...options };
916
+ try {
917
+ const canvas = await html2canvas__default.default(document.body, {
918
+ x: window.scrollX,
919
+ y: window.scrollY,
920
+ width: window.innerWidth,
921
+ height: window.innerHeight,
922
+ useCORS: true,
923
+ allowTaint: true,
924
+ logging: false
925
+ });
926
+ const out = downscale(canvas, maxWidth);
927
+ const mime = format === "jpeg" ? "image/jpeg" : "image/png";
928
+ return out.toDataURL(mime, format === "jpeg" ? quality : void 0);
929
+ } catch {
930
+ return void 0;
931
+ }
932
+ }
933
+ function dataUrlToBase64(dataUrl) {
934
+ return dataUrl.replace(/^data:image\/\w+;base64,/, "");
935
+ }
936
+
937
+ // src/storage.ts
938
+ function getDefaultWebStorage() {
939
+ try {
940
+ if (typeof localStorage === "undefined") return void 0;
941
+ const probe = "__outcode_bug_reporter_probe__";
942
+ localStorage.setItem(probe, "1");
943
+ localStorage.removeItem(probe);
944
+ return {
945
+ getItem: (key) => localStorage.getItem(key),
946
+ setItem: (key, value) => localStorage.setItem(key, value),
947
+ removeItem: (key) => localStorage.removeItem(key)
948
+ };
949
+ } catch {
950
+ return void 0;
951
+ }
952
+ }
953
+ function SuccessCard({ ticket, ticketTitle, backendName, onDone }) {
954
+ return /* @__PURE__ */ jsxRuntime.jsx(
955
+ "div",
956
+ {
957
+ style: {
958
+ position: "fixed",
959
+ inset: 0,
960
+ zIndex: 2147483100,
961
+ display: "flex",
962
+ alignItems: "center",
963
+ justifyContent: "center",
964
+ background: "rgba(8,10,18,0.5)",
965
+ animation: "ocbr-fade .25s",
966
+ fontFamily: FONT
967
+ },
968
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
969
+ "div",
970
+ {
971
+ style: {
972
+ width: 344,
973
+ maxWidth: "90vw",
974
+ background: "var(--panel)",
975
+ border: "1px solid var(--border)",
976
+ borderRadius: 18,
977
+ boxShadow: "var(--shadow)",
978
+ padding: 28,
979
+ textAlign: "center",
980
+ color: "var(--text)",
981
+ animation: "ocbr-pop .35s cubic-bezier(.2,1.1,.3,1)"
982
+ },
983
+ children: [
984
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 56, height: 56, borderRadius: "50%", background: "var(--ok-bg)", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 13l5 5L20 6", stroke: "var(--ok)", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
985
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 18, fontWeight: 660, letterSpacing: "-0.01em" }, children: "Report filed" }),
986
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 13.5, color: "var(--muted)", marginTop: 5, lineHeight: 1.45 }, children: [
987
+ "Tracked in ",
988
+ backendName,
989
+ " and routed to the right team automatically."
990
+ ] }),
991
+ ticket && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 9, margin: "18px 0", padding: 11, background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 10, fontFamily: MONO, fontSize: 13 }, children: [
992
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--accent)", fontWeight: 600 }, children: ticket }),
993
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--faint)" }, children: "\xB7" }),
994
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 180 }, children: ticketTitle })
995
+ ] }),
996
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 8, marginTop: ticket ? 0 : 18 }, children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onDone, style: { flex: 1, padding: 11, borderRadius: 10, border: "1px solid var(--border)", background: "var(--surface)", color: "var(--text)", fontFamily: "inherit", fontSize: 13.5, fontWeight: 600, cursor: "pointer" }, children: "Done" }) })
997
+ ]
998
+ }
999
+ )
1000
+ }
1001
+ );
1002
+ }
1003
+ function shortUA() {
1004
+ if (typeof navigator === "undefined") return "\u2014";
1005
+ const ua = navigator.userAgent;
1006
+ const m = ua.match(/(Chrome|Firefox|Safari|Edg)\/[\d.]+/);
1007
+ const os = /Mac/.test(ua) ? "macOS" : /Win/.test(ua) ? "Windows" : /Linux/.test(ua) ? "Linux" : "";
1008
+ return [m ? m[0].replace("Edg", "Edge") : "Browser", os].filter(Boolean).join(" \xB7 ");
1009
+ }
1010
+ function buildMeta(config) {
1011
+ const rows = [];
1012
+ if (typeof location !== "undefined") rows.push({ label: "Route", value: location.pathname + location.search });
1013
+ rows.push({ label: "Browser", value: shortUA() });
1014
+ if (typeof window !== "undefined") rows.push({ label: "Viewport", value: `${window.innerWidth} \xD7 ${window.innerHeight} @${window.devicePixelRatio || 1}x` });
1015
+ if (typeof navigator !== "undefined") {
1016
+ let tz = "";
1017
+ try {
1018
+ tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
1019
+ } catch {
1020
+ }
1021
+ rows.push({ label: "Locale", value: [navigator.language, tz].filter(Boolean).join(" \xB7 ") });
1022
+ }
1023
+ rows.push({ label: "Release", value: config.appVersion ? `${config.appName}@${config.appVersion}` : config.appName });
1024
+ if (typeof navigator !== "undefined") {
1025
+ if (navigator.language) rows.push({ label: "Language", value: navigator.language });
1026
+ const conn = navigator.connection;
1027
+ if (conn) {
1028
+ const parts = [conn.type, conn.effectiveType, conn.saveData ? "data-saver" : void 0].filter(Boolean);
1029
+ if (parts.length) rows.push({ label: "Connection", value: parts.join(" \xB7 ") });
1030
+ }
1031
+ if (typeof navigator.onLine === "boolean") rows.push({ label: "Online", value: navigator.onLine ? "online" : "offline" });
1032
+ const mem = navigator.deviceMemory;
1033
+ if (mem) rows.push({ label: "Memory", value: `${mem} GB` });
1034
+ if (navigator.hardwareConcurrency) rows.push({ label: "CPU", value: `${navigator.hardwareConcurrency} cores` });
1035
+ }
1036
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
1037
+ const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
1038
+ const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1039
+ rows.push({ label: "Scheme", value: [dark ? "dark" : "light", reduced ? "reduced-motion" : void 0].filter(Boolean).join(" \xB7 ") });
1040
+ }
1041
+ if (typeof screen !== "undefined" && screen.width) {
1042
+ rows.push({ label: "Screen", value: `${screen.width} \xD7 ${screen.height} \xB7 ${screen.colorDepth}-bit` });
1043
+ }
1044
+ const diag = config.collectDiagnostics?.();
1045
+ const crumbs = diag?.breadcrumbs ?? [];
1046
+ const errs = crumbs.filter((b) => b.level === "error").length;
1047
+ const warns = crumbs.filter((b) => b.level === "warn").length;
1048
+ if (errs || warns) rows.push({ label: "Console", value: `${errs} error${errs === 1 ? "" : "s"} \xB7 ${warns} warning${warns === 1 ? "" : "s"}`, flag: errs ? "err" : "warn" });
1049
+ if (diag?.lastFailedApiCall) rows.push({ label: "Network", value: "1 failed request", flag: "warn" });
1050
+ return rows;
1051
+ }
1052
+ function BugReporterButton({ config, label = "Report a bug", style }) {
1053
+ const theme = react.useMemo(() => bugReporterCore.resolveTheme(config.theme), [config.theme]);
1054
+ const backendName = config.backendName ?? "ClickUp";
1055
+ const [step, setStep] = react.useState("idle");
1056
+ const [screenshot, setScreenshot] = react.useState();
1057
+ const [edited, setEdited] = react.useState();
1058
+ const [annCount, setAnnCount] = react.useState(0);
1059
+ const [title, setTitle] = react.useState("");
1060
+ const [desc, setDesc] = react.useState("");
1061
+ const [severity, setSeverity] = react.useState("medium");
1062
+ const [type, setType] = react.useState("bug");
1063
+ const [ticket, setTicket] = react.useState();
1064
+ const [error, setError] = react.useState();
1065
+ const [meta, setMeta] = react.useState([]);
1066
+ react.useEffect(() => {
1067
+ ensureKeyframes();
1068
+ const storage = config.storage ?? getDefaultWebStorage();
1069
+ if (storage) void new bugReporterCore.ReportQueue(storage).flush(config);
1070
+ }, []);
1071
+ const queue = react.useMemo(() => {
1072
+ const storage = config.storage ?? getDefaultWebStorage();
1073
+ return storage ? new bugReporterCore.ReportQueue(storage) : void 0;
1074
+ }, [config.storage]);
1075
+ const reset = () => {
1076
+ setStep("idle");
1077
+ setScreenshot(void 0);
1078
+ setEdited(void 0);
1079
+ setAnnCount(0);
1080
+ setTitle("");
1081
+ setDesc("");
1082
+ setSeverity("medium");
1083
+ setType("bug");
1084
+ setTicket(void 0);
1085
+ setError(void 0);
1086
+ };
1087
+ const open = async () => {
1088
+ if (step !== "idle") return;
1089
+ setStep("capturing");
1090
+ const shot = await captureViewport(config.screenshot);
1091
+ setScreenshot(shot);
1092
+ setStep("annotate");
1093
+ };
1094
+ const onContinue = (flattened, count) => {
1095
+ setEdited(flattened);
1096
+ setAnnCount(count);
1097
+ setMeta(buildMeta(config));
1098
+ setStep("form");
1099
+ if (config.collectContext) {
1100
+ void (async () => {
1101
+ try {
1102
+ const extra = bugReporterCore.normalizeContext(await config.collectContext());
1103
+ if (extra.length) setMeta((m) => [...m, ...extra]);
1104
+ } catch {
1105
+ }
1106
+ })();
1107
+ }
1108
+ };
1109
+ const submit = async () => {
1110
+ if (!title.trim()) return;
1111
+ setStep("submitting");
1112
+ setError(void 0);
1113
+ const shot = edited ?? screenshot;
1114
+ const options = {
1115
+ title: title.trim(),
1116
+ description: desc.trim(),
1117
+ screenshotBase64: shot ? dataUrlToBase64(shot) : void 0,
1118
+ isReportingProblem: true,
1119
+ severity,
1120
+ type,
1121
+ context: meta.map((m) => ({ label: m.label, value: m.value })),
1122
+ metadata: { platform: "web", userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0 }
1123
+ };
1124
+ try {
1125
+ const res = await bugReporterCore.submitReport(config, options);
1126
+ if (res.success) {
1127
+ setTicket(res.id);
1128
+ setStep("done");
1129
+ } else if (queue) {
1130
+ await queue.enqueue(options);
1131
+ setTicket(void 0);
1132
+ setStep("done");
1133
+ } else {
1134
+ setError(res.message || "Submission failed.");
1135
+ setStep("form");
1136
+ }
1137
+ } catch (e) {
1138
+ if (queue) {
1139
+ await queue.enqueue(options);
1140
+ setStep("done");
1141
+ } else {
1142
+ setError(e instanceof Error ? e.message : "Submission failed.");
1143
+ setStep("form");
1144
+ }
1145
+ }
1146
+ };
1147
+ const submitting = step === "submitting";
1148
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...cssVars(theme), fontFamily: FONT }, children: [
1149
+ step === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1150
+ /* @__PURE__ */ jsxRuntime.jsx(
1151
+ "div",
1152
+ {
1153
+ style: {
1154
+ position: "fixed",
1155
+ bottom: 86,
1156
+ right: 26,
1157
+ background: "var(--panel)",
1158
+ border: "1px solid var(--border)",
1159
+ borderRadius: 11,
1160
+ padding: "8px 13px",
1161
+ fontSize: 12.5,
1162
+ fontWeight: 600,
1163
+ color: "var(--text)",
1164
+ boxShadow: "var(--shadow)",
1165
+ zIndex: 2147482900,
1166
+ whiteSpace: "nowrap",
1167
+ animation: "ocbr-float 3s ease-in-out infinite",
1168
+ pointerEvents: "none"
1169
+ },
1170
+ children: "Spotted a bug? Tap \u2192"
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ jsxRuntime.jsxs(
1174
+ "button",
1175
+ {
1176
+ type: "button",
1177
+ "aria-label": label,
1178
+ onClick: open,
1179
+ style: {
1180
+ position: "fixed",
1181
+ bottom: 26,
1182
+ right: 26,
1183
+ width: 56,
1184
+ height: 56,
1185
+ borderRadius: "50%",
1186
+ background: "var(--accent)",
1187
+ border: "none",
1188
+ cursor: "pointer",
1189
+ display: "flex",
1190
+ alignItems: "center",
1191
+ justifyContent: "center",
1192
+ boxShadow: "0 10px 28px rgba(40,40,90,0.34)",
1193
+ zIndex: 2147482950,
1194
+ ...style
1195
+ },
1196
+ children: [
1197
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", inset: 0, borderRadius: "50%", border: "2px solid var(--accent)", animation: "ocbr-ping 1.9s ease-out infinite" } }),
1198
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [
1199
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11a4 4 0 018 0v3a4 4 0 01-8 0z", stroke: "#fff", strokeWidth: "1.7" }),
1200
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7.2", r: "2", stroke: "#fff", strokeWidth: "1.6" }),
1201
+ /* @__PURE__ */ jsxRuntime.jsx("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" })
1202
+ ] })
1203
+ ]
1204
+ }
1205
+ )
1206
+ ] }),
1207
+ step === "capturing" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", inset: 0, background: "#fff", zIndex: 2147483200, animation: "ocbr-flash .7s ease-out forwards", pointerEvents: "none" } }),
1208
+ step === "annotate" && screenshot && /* @__PURE__ */ jsxRuntime.jsx(AnnotateOverlay, { src: screenshot, onCancel: reset, onContinue }),
1209
+ (step === "form" || step === "submitting") && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 2147483050, fontFamily: FONT }, children: [
1210
+ /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: reset, style: { position: "absolute", inset: 0, background: "rgba(8,10,18,0.5)", animation: "ocbr-fade .25s" }, children: (edited ?? screenshot) && /* @__PURE__ */ jsxRuntime.jsx("img", { src: edited ?? screenshot, alt: "", style: { width: "100%", height: "100%", objectFit: "cover", opacity: 0.25, filter: "blur(1px)" } }) }),
1211
+ /* @__PURE__ */ jsxRuntime.jsx(
1212
+ "div",
1213
+ {
1214
+ style: {
1215
+ position: "absolute",
1216
+ top: 0,
1217
+ right: 0,
1218
+ bottom: 0,
1219
+ width: "min(400px, 100vw)",
1220
+ boxShadow: "-14px 0 44px rgba(10,12,30,0.20)",
1221
+ borderLeft: "1px solid var(--border)",
1222
+ animation: "ocbr-panel-in .3s cubic-bezier(.2,.8,.2,1)"
1223
+ },
1224
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1225
+ BugReportForm,
1226
+ {
1227
+ screenshotUrl: edited ?? screenshot,
1228
+ annotationCount: annCount,
1229
+ title,
1230
+ description: desc,
1231
+ severity,
1232
+ type,
1233
+ meta,
1234
+ backendName,
1235
+ submitting,
1236
+ error,
1237
+ onTitle: setTitle,
1238
+ onDescription: setDesc,
1239
+ onSeverity: setSeverity,
1240
+ onType: setType,
1241
+ onBack: () => setStep("annotate"),
1242
+ onClose: reset,
1243
+ onEditAnnotations: () => setStep("annotate"),
1244
+ onSubmit: submit
1245
+ }
1246
+ )
1247
+ }
1248
+ )
1249
+ ] }),
1250
+ step === "done" && /* @__PURE__ */ jsxRuntime.jsx(
1251
+ SuccessCard,
1252
+ {
1253
+ ticket,
1254
+ ticketTitle: title.trim() || "Untitled report",
1255
+ backendName,
1256
+ onDone: reset
1257
+ }
1258
+ )
1259
+ ] });
1260
+ }
1261
+
1262
+ exports.AnnotateOverlay = AnnotateOverlay;
1263
+ exports.AnnotateToolbar = AnnotateToolbar;
1264
+ exports.BugReportForm = BugReportForm;
1265
+ exports.BugReporterButton = BugReporterButton;
1266
+ exports.SuccessCard = SuccessCard;
1267
+ exports.captureViewport = captureViewport;
1268
+ exports.cssVars = cssVars;
1269
+ exports.dataUrlToBase64 = dataUrlToBase64;
1270
+ exports.ensureKeyframes = ensureKeyframes;
1271
+ exports.flattenAnnotations = flattenAnnotations;
1272
+ exports.getDefaultWebStorage = getDefaultWebStorage;
1273
+ //# sourceMappingURL=index.js.map
1274
+ //# sourceMappingURL=index.js.map